You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix field/behavior mismatches in enrichment, alert, change, incident, and template skill cards
Correct hand-written prose that drifted from actual CLI/API behavior:
- enrichment: rewrite the rule-kind settings table to match the real
upsert schema (source_field/result_label/pattern|g_json for
extraction, result_label/template for composition, mapping_type +
schema_id|api_id for mapping, drop_labels for drop); fix example
payloads that used non-existent field names; document that
mapping-data-upload truncates existing data by default (no
append/no-truncate flag) and that mapping-data-delete caps at 100
keys per call.
- alert: correct a false claim that `list` returns a total/page
footer in structured output (it's a bare top-level array) and point
to --fields projection or a wrapper-style verb instead; document
pipeline rule settings shapes, the 50-rule cap, and that pipeline
`if`/`source_filters` is OR-of-AND (unlike enrichment's flat AND
list).
- change: remove `-1h` from the --since/--until example since the
shared time parser rejects negative durations; add the list window
cap (31 days / 100 results) already documented for incident.
- incident: correct the claim that `detail` and `get` reject a 6-char
short id — both auto-resolve it via a 30-day lookback, unlike every
other positional-id verb; add missing routing rows for `assign`,
`responder-add`, and the nested `war-room <verb>` subcommand form.
- template: document that --feishu-app-card-table-enabled uses
pointer semantics on update (patches only when explicitly set),
unlike the plain string channel-content fields.
All edits are confined to hand-written prose outside the
GENERATED:* fences; `skilldoc check` still passes unchanged.
Copy file name to clipboardExpand all lines: skills/flashduty/reference/alert.md
+6-2Lines changed: 6 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -143,14 +143,18 @@ View alert timeline
143
143
144
144
## Pipeline rule kinds
145
145
146
-
`pipeline-upsert` replaces the whole pipeline; `rules[].kind` values: `title_reset` · `description_reset` · `severity_reset` · `alert_drop` · `alert_inhibit`. The `rules` array has no typed flag — pass it via `--data '{"rules":[...]}'`. The call is idempotent (upsert), so re-running with the same body is safe.
146
+
`pipeline-upsert` replaces the whole pipeline (max 50 rules); `rules[].kind` values: `title_reset` · `description_reset` · `severity_reset` · `alert_drop` · `alert_inhibit`. The `rules` array has no typed flag — pass it via `--data '{"rules":[...]}'`. The call is idempotent (upsert), so re-running with the same body is safe.
**`rules[].if` (and `alert_inhibit`'s `source_filters`) is OR-of-AND** — an array of arrays of conditions: the outer array is OR, each inner array is AND. This is unlike `enrichment`'s rule-level `if`, which is a single flat AND-only condition list.
147
151
148
152
## Gotchas
149
153
150
154
-**All alert verbs are positional except `list` and the two-ID `merge` flag.** Every verb with `<alert-id>` in its `use` form takes that ID as the first bare argument — do NOT pass `--alert-id`. The single exception: `merge` takes the first alert ID positionally AND requires `--incident-id` as a flag (two different IDs, different roles).
151
155
-**`alert get` vs `alert info`, `alert events` vs `alert-event list`:** both pairs exist; prefer `get`/`events` (shorter, no extra flag); `info`/`event-list` accept `--alert-id` as a flag override for scripting.
152
156
-**No server-side title filter on `list`.** To search by title, use `--json` and pipe to `jq`: `fduty alert list --json | jq '.[] | select(.title | test("disk";"i"))'`
153
-
-**If `list`returns a `total`, use it.** Do not paginate page 1/2/3... just to count alerts. Ask the narrowest question (`--active`, `--recovered`, `--severity`, `--channel`, `--since`) and read the server-reported total for that bucket.
157
+
-**`list`'s structured output has no `total`/page metadata** — its `--json`/`toon` response is a bare TOP-LEVEL array (see the `list`fence entry above), not a `{items, total}` wrapper. To count matches, project the narrowest field with `--fields` and count elements, or use a wrapper-style verb whose fence shows `total` (e.g. `list-by-ids`). Don't paginate page 1/2/3... just to count alerts — narrow the query instead (`--active`, `--recovered`, `--severity`, `--channel`, `--since`).
154
158
-**Use `--fields` when hunting IDs, not full rows.** If the task is "find alert IDs / titles / channels / severities", project only those fields first, then drill into one alert with `get` / `events`. Dumping every field for 100 alerts wastes tokens and hides the one row you need.
155
159
-**`list` time window cap is 31 days**; `--limit` max is 100. For broader queries use `insight` domain.
156
160
-**`pipeline-upsert` fully replaces** the existing pipeline — always fetch current config with `pipeline-info` first and include unchanged rules in the new body.
Copy file name to clipboardExpand all lines: skills/flashduty/reference/change.md
+2-1Lines changed: 2 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -40,11 +40,12 @@ List changes
40
40
## Key concepts
41
41
42
42
-**Correlation is heuristic, not relational.** A change is "related" to an incident when their `labels` overlap and their timestamps are close — there is no `incident_id` on a change. Judge the overlap yourself; do not claim a causal link the data doesn't support.
43
-
-**`--integration` / `--channel`** scope to the source that emitted the change; **`--since` / `--until`** bound the window (relative like `24h`, `-1h`, `now`, or Unix seconds).
43
+
-**`--integration` / `--channel`** scope to the source that emitted the change; **`--since` / `--until`** bound the window (a non-negative duration like `24h`, a future offset like `+24h`, `now`, a date/datetime, or Unix seconds — negative durations like `-1h` are rejected).
44
44
45
45
## Gotchas
46
46
47
47
-**List-only domain.** There is no `change get` / `change detail` verb — `list` (with filters) is the whole surface. Don't guess a detail verb.
48
+
-**`list` window cap**: `--since`/`--until` window must be < 31 days; `--limit` max 100.
48
49
-**Empty result is authoritative** — no changes in that window/scope. Report it; don't widen blindly or invent a change to explain the incident.
Copy file name to clipboardExpand all lines: skills/flashduty/reference/enrichment.md
+9-7Lines changed: 9 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
# fduty enrichment — command card
2
2
3
-
Prereq: `SKILL.md` read. Read verbs are free. **`upsert` fully replaces all rules for an integration** (atomic, irreversible in the sense that the previous ruleset is gone); `mapping-schema-delete`, `mapping-api-delete`, and `mapping-data-truncate` are irreversible — confirm IDs before running.
3
+
Prereq: `SKILL.md` read. Read verbs are free. **`upsert` fully replaces all rules for an integration** (atomic, irreversible in the sense that the previous ruleset is gone); `mapping-schema-delete`, `mapping-api-delete`, `mapping-data-truncate`, and `mapping-data-upload` are irreversible — confirm IDs before running.
4
4
5
5
## Route here when
6
6
@@ -64,7 +64,7 @@ fduty enrichment info <integration-id> --output-format toon
64
64
65
65
# 3. Upsert rules (full replacement; rules array via --data)
fduty enrichment info <integration-id> --output-format toon
@@ -194,10 +194,10 @@ Upsert enrichment rules
194
194
195
195
| kind | what it does | key `settings` fields |
196
196
|---|---|---|
197
-
|`extraction`| extracts a new label via regex or GJson path |`source`, `target`, `method` (`regex`/`gjson`), `pattern`|
198
-
|`composition`| builds a label from a Go template over existing labels |`target`, `template`|
199
-
|`mapping`| looks up result labels from a schema or API by source label values |`schema_id`OR `api_id`, `source_labels`, `result_labels`|
200
-
|`drop`| removes labels matching a list |`labels`|
197
+
|`extraction`| extracts a new label via regex or GJson path |`source_field`, `result_label`, `pattern` XOR `g_json`, optional `override`|
198
+
|`composition`| builds a label from a Go template over existing labels |`result_label`, `template`, optional `override`|
199
+
|`mapping`| looks up result labels from a schema or external API |`mapping_type` (`schema`\|`api`),`schema_id`(when `schema`) or `api_id` (when `api`), `result_labels` — the mapping schema's own `source_labels` drive the lookup; there is no rule-level source-label field|
200
+
|`drop`| removes labels matching a list |`drop_labels`|
201
201
202
202
Each rule may have an optional `if` AND-filter: `[{"key":"env","oper":"IN","vals":["prod"]}]` — rule is skipped when the filter does not match. `oper` must be `IN` or `NOTIN`.
203
203
@@ -209,6 +209,8 @@ Each rule may have an optional `if` AND-filter: `[{"key":"env","oper":"IN","vals
209
209
-**`mapping-data-upsert` requires `docs` via `--data`** — this array cannot be expressed as flat flags. Each doc must include all `source_labels` AND all `result_labels` fields for the schema, or the row is rejected.
210
210
-**`mapping-schema-create` requires Pro plan** — creating a schema on a free account returns a plan-gate error, not a 404.
211
211
-**`mapping-data-truncate` wipes all rows immediately** — there is no undo. Use `mapping-data-download` to export a backup CSV first if the data matters.
212
+
-**`mapping-data-upload` truncates existing schema data before loading the new CSV rows** — this is the documented default behavior, and there is no append/no-truncate flag. Treat it like `mapping-data-truncate` followed by a bulk load; use `mapping-data-download` to back up first if the data matters.
213
+
-**`mapping-data-delete` accepts at most 100 keys per call** — batch larger deletes into multiple calls.
212
214
-**`source-labels` and `result-labels` must not overlap** on `mapping-schema-create`; max 3 source labels, max 10 result labels. Violating either constraint 400s.
213
215
214
216
## Worked example — inspect and extend enrichment rules
@@ -218,5 +220,5 @@ Each rule may have an optional `if` AND-filter: `[{"key":"env","oper":"IN","vals
218
220
fduty enrichment info 42 --output-format toon
219
221
# → copy the existing rules[] array, append the new rule, then upsert the full set:
"告警 / 故障 / 事件 / 响应 / 值班 / incident / page / outage / triage / acknowledge / resolve / snooze / escalate / post-mortem" → **incident**, NOT `alert` (alert = deduplicated signal; incident = actionable item responders work). NOT `insight` (metrics/MTTA/MTTR). You need **`incident_id` (24-char MongoDB ObjectID)** for most verbs — not the 6-char `num` shown in the UI. If you only have a num, use `incident info --num <num>` first.
7
+
"告警 / 故障 / 事件 / 响应 / 值班 / incident / page / outage / triage / acknowledge / resolve / snooze / escalate / post-mortem" → **incident**, NOT `alert` (alert = deduplicated signal; incident = actionable item responders work). NOT `insight` (metrics/MTTA/MTTR). You need **`incident_id` (24-char MongoDB ObjectID)** for most verbs — not the 6-char `num` shown in the UI. **`detail` and `get` are the exception and accept either** (a num auto-resolves via a 30-day lookback). For any other verb, if you only have a num, use `incident info --num <num>` first.
| war room (IM chat) |`war-room-list <incident-id>` → `war-room-create <incident-id>`|
43
+
| war room (IM chat), nested subcommand form |`war-room list/create/get/add-member/default-observers/delete <id>`|
41
44
42
45
## Hot flow — triage an active incident
43
46
@@ -481,7 +484,7 @@ List war rooms
481
484
482
485
## Gotchas
483
486
484
-
-**24-char `incident_id` vs 6-char `num`**: positional-id verbs (`ack`, `close`, `resolve`, `detail`, `alerts`, `timeline`, `merge`, `reassign`, `comment`, `reset`, …) require the full ObjectID. Passing a 6-char num 400s. Use `incident info --num <num>` to resolve, or `incident list --query <num>` and read `incident_id`.
487
+
-**24-char `incident_id` vs 6-char `num`**: most positional-id verbs (`ack`, `close`, `resolve`, `alerts`, `timeline`, `merge`, `reassign`, `comment`, `reset`, …) require the full ObjectID. Passing a 6-char num to any of them 400s. Use `incident info --num <num>` to resolve, or `incident list --query <num>` and read `incident_id`. **Exception: `detail` and `get` accept either form** — a 6-char num auto-resolves against the last 30 days via `/incident/list`; a miss errors `no incident with short id ... in the last 30 days`, and multiple matches list full-id candidates to disambiguate.
485
488
-**`similar` only works on channel-backed incidents** (those with a real `channel_id`). Manually created incidents with no channel return HTTP 400 "Channel not found" — this is expected, not transient. Fall back to `incident list --query "<keywords>"` for text search.
486
489
-**`update` vs `reset`**: `update <id>` edits title/description/severity/custom fields. `reset <incident-id>` additionally supports `--impact`, `--root-cause`, `--resolution` (the AI narrative fields). Use `reset` for post-incident write-back.
487
490
-**If `list` returns a `total`, use it instead of page-walking.** For "how many incidents are Triggered / Processing / Closed", run one filtered `incident list --progress <bucket> ...` per bucket and read the returned `total`. Do not fetch page 1/2/3 just to derive counts the server already computed.
-**`info`, `update`, `delete` take `<template-id>` as a positional first argument** — pass it bare, not as `--template-id`. `create`, `list`, `preview`, `validate`, `get-preset`, `functions`, `variables` take all inputs as flags.
167
167
-**`update` replaces every channel field you pass — omitted channel flags are left unchanged** (server behavior: only supplied fields overwrite). Always pass `--template-name` even if the name is unchanged — it is required on update.
168
+
-**`--feishu-app-card-table-enabled` uses pointer semantics on `update`** — unlike the plain string channel-content flags, it patches the table-rendering setting only when the flag is explicitly passed; omit it to leave the existing setting untouched. It is a plain bool on `create` (no prior setting to preserve).
168
169
-**`delete` is permanent.** The built-in preset (`template_id = 000000000000000000000001`) can be addressed by that sentinel ID in `info` and `delete` — don't delete it.
169
170
-**`validate` reads from a local `--file`; `preview` takes inline `--content`.** They are complementary: `validate` gives size-vs-limit diagnostics; `preview` renders against real or mock incident data.
170
171
-**`email` uses `html/template` syntax; `sms` and `voice` use `text/template`** — auto-escaping rules differ. Don't mix them.
0 commit comments