Skip to content

Commit 7060c51

Browse files
committed
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.
1 parent e8b8e77 commit 7060c51

5 files changed

Lines changed: 23 additions & 12 deletions

File tree

skills/flashduty/reference/alert.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,14 +143,18 @@ View alert timeline
143143

144144
## Pipeline rule kinds
145145

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.
147+
148+
`settings` shape depends on `kind`: `title_reset``{"title": "<template>"}`; `description_reset``{"description": "<template>"}`; `severity_reset``{"severity": "Critical"|"Warning"|"Info"}`; `alert_drop``{}` (empty object); `alert_inhibit``{"equals": ["<label_key>", ...], "source_filters": <OrFilterGroup>}`.
149+
150+
**`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.
147151

148152
## Gotchas
149153

150154
- **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).
151155
- **`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.
152156
- **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`).
154158
- **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.
155159
- **`list` time window cap is 31 days**; `--limit` max is 100. For broader queries use `insight` domain.
156160
- **`pipeline-upsert` fully replaces** the existing pipeline — always fetch current config with `pipeline-info` first and include unchanged rules in the new body.

skills/flashduty/reference/change.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,12 @@ List changes
4040
## Key concepts
4141

4242
- **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).
4444

4545
## Gotchas
4646

4747
- **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.
4849
- **Empty result is authoritative** — no changes in that window/scope. Report it; don't widen blindly or invent a change to explain the incident.
4950

5051
## Worked example

skills/flashduty/reference/enrichment.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# fduty enrichment — command card
22

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.
44

55
## Route here when
66

@@ -64,7 +64,7 @@ fduty enrichment info <integration-id> --output-format toon
6464

6565
# 3. Upsert rules (full replacement; rules array via --data)
6666
fduty enrichment upsert <integration-id> \
67-
--data '{"rules":[{"kind":"mapping","settings":{"schema_id":"<schema-id>","source_labels":["service"],"result_labels":["owner_team","oncall_email"]}},{"kind":"composition","settings":{"target":"summary","template":"[{{.owner_team}}] {{.title}}"}}]}'
67+
--data '{"rules":[{"kind":"mapping","settings":{"mapping_type":"schema","schema_id":"<schema-id>","result_labels":["owner_team","oncall_email"]}},{"kind":"composition","settings":{"result_label":"summary","template":"[{{.owner_team}}] {{.title}}"}}]}'
6868

6969
# 4. Confirm the new ruleset
7070
fduty enrichment info <integration-id> --output-format toon
@@ -194,10 +194,10 @@ Upsert enrichment rules
194194

195195
| kind | what it does | key `settings` fields |
196196
|---|---|---|
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` |
201201

202202
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`.
203203

@@ -209,6 +209,8 @@ Each rule may have an optional `if` AND-filter: `[{"key":"env","oper":"IN","vals
209209
- **`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.
210210
- **`mapping-schema-create` requires Pro plan** — creating a schema on a free account returns a plan-gate error, not a 404.
211211
- **`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.
212214
- **`source-labels` and `result-labels` must not overlap** on `mapping-schema-create`; max 3 source labels, max 10 result labels. Violating either constraint 400s.
213215

214216
## 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
218220
fduty enrichment info 42 --output-format toon
219221
# → copy the existing rules[] array, append the new rule, then upsert the full set:
220222
fduty enrichment upsert 42 \
221-
--data '{"rules":[<existing_rules...>,{"kind":"drop","settings":{"labels":["raw_body","_meta"]}}]}'
223+
--data '{"rules":[<existing_rules...>,{"kind":"drop","settings":{"drop_labels":["raw_body","_meta"]}}]}'
222224
```

skills/flashduty/reference/incident.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Prereq: `SKILL.md` read. Read verbs are free. **Mutating verbs notify responders
44

55
## Route here when
66

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. 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.
88

99
## Intent → verb
1010

@@ -32,12 +32,15 @@ Prereq: `SKILL.md` read. Read verbs are free. **Mutating verbs notify responders
3232
| snooze / un-snooze | `snooze <id> [<id2>...]` / `wake <incident-id> [<id2>...]` |
3333
| add comment | `comment <id> [<id2>...]` |
3434
| add responder by member ID | `add-responder <id>` |
35+
| add responders (alternate; positional person IDs) | `responder-add <person-id> [<id2>...] --incident-id <id>` |
36+
| dispatch to an escalation level / responder | `assign --data '{"incident_id":"<id>","assigned_to":{...}}'` (body-only `assigned_to`) |
3537
| replace responder list | `reassign <id>` |
3638
| merge duplicates (IRREVERSIBLE) | `merge <target_id>` |
3739
| stop auto-merging alerts in | `disable-merge <incident-id> [<id2>...]` |
3840
| permanently delete (IRREVERSIBLE) | `remove <id> [<id2>...]` |
3941
| post-mortem reports | `post-mortem-list` / `post-mortem-info <post-mortem-id>` / `post-mortem-delete <post-mortem-id>` |
4042
| 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>` |
4144

4245
## Hot flow — triage an active incident
4346

@@ -481,7 +484,7 @@ List war rooms
481484

482485
## Gotchas
483486

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.
485488
- **`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.
486489
- **`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.
487490
- **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.

skills/flashduty/reference/template.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ Note: `create` / `update` flags use **hyphenated** names (`--dingtalk-app`, `--f
165165

166166
- **`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.
167167
- **`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).
168169
- **`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.
169170
- **`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.
170171
- **`email` uses `html/template` syntax; `sms` and `voice` use `text/template`** — auto-escaping rules differ. Don't mix them.

0 commit comments

Comments
 (0)