Skip to content

Commit 6eb0721

Browse files
committed
docs: guide flashduty large-list scans
1 parent 45677ff commit 6eb0721

4 files changed

Lines changed: 58 additions & 0 deletions

File tree

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package skilldoc
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"strings"
7+
"testing"
8+
)
9+
10+
func TestFlashdutySkillGuidesLargeListScans(t *testing.T) {
11+
body := readSkillFile(t, "SKILL.md")
12+
incident := readSkillFile(t, "reference", "incident.md")
13+
insight := readSkillFile(t, "reference", "insight.md")
14+
15+
for _, want := range []string{
16+
"Large list scans",
17+
"redirect JSON to a temp file",
18+
"Do not dump page-sized JSON",
19+
} {
20+
if !strings.Contains(body, want) {
21+
t.Errorf("SKILL.md missing large-list guidance %q", want)
22+
}
23+
}
24+
for _, want := range []string{
25+
"Do not re-run `incident alerts`, `incident similar`, `incident timeline`, or `change list`",
26+
"Page-sized scans go to files",
27+
} {
28+
if !strings.Contains(incident, want) {
29+
t.Errorf("incident card missing large-list/summary guidance %q", want)
30+
}
31+
}
32+
for _, want := range []string{
33+
"For multi-dimensional reports, fetch once to a temp file",
34+
"Do not run the same account-wide list again for each `jq` bucket",
35+
} {
36+
if !strings.Contains(insight, want) {
37+
t.Errorf("insight card missing reusable JSON guidance %q", want)
38+
}
39+
}
40+
}
41+
42+
func readSkillFile(t *testing.T, parts ...string) string {
43+
t.Helper()
44+
p := filepath.Join(append([]string{"..", "..", "skills", "flashduty"}, parts...)...)
45+
b, err := os.ReadFile(p)
46+
if err != nil {
47+
t.Fatalf("read %s: %v", p, err)
48+
}
49+
return string(b)
50+
}

skills/flashduty/SKILL.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ Append `--output-format toon` to read commands: it drops the per-row repeated ke
2929

3030
**Shape the payload before you fetch it.** For ID scans, counts, or "find the matching row" tasks, prefer `--fields` projections and compact list verbs over full detail dumps. Huge raw JSON dumps are a last resort, not a default.
3131

32+
**Large list scans: file first, summarize second.** When you truly need `--json` for `jq` over a page-sized list (`--limit 100`, broad channel/account windows, incident/alert/on-call reports), redirect JSON to a temp file and emit only counts or selected fields: `fduty ... --json > /tmp/fduty_rows.json && jq '...' /tmp/fduty_rows.json`. Do not dump page-sized JSON into the chat transcript, and do not run the same list again for each aggregation bucket.
33+
3234
**Empty result = authoritative not-found.** A filter returning `[]` means no such entity in scope — report it (optionally the 1–2 closest names) and stop. Do **not** brute-force (no shifted-keyword re-queries, no widening past caps, no full-dump grep). Never infer "feature not enabled" from an empty list, and never fabricate data absent from tool output.
3335

3436
**A result you did not fetch is "unknown", never "empty".** You may report a command's result — including "returned empty" or any count/list/finding — **only if that exact command appears in your tool-call history this turn**. If you did not run it, the honest answer is "未查询 / not queried", followed by the command to run. Writing "`incident similar` 返回空" or "无变更" for a command you never executed is fabrication, not a summary.

skills/flashduty/reference/incident.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ bash <skill-dir>/scripts/incident-summary.sh <incident-id>
7373

7474
`<skill-dir>` is this skill's base directory — you were given it when the skill loaded (it is also the folder you read this card from). The script runs every command below and prints the results in one block, so each section of your summary is backed by real output and there is nothing to guess. (To tie post-mortems to *this* incident, re-run `incident post-mortem-list --channel-ids <channel-id>` with the `channel_id` from `detail`.)
7575

76+
Do not re-run `incident alerts`, `incident similar`, `incident timeline`, or `change list` after `incident-summary.sh` unless the script output is missing/truncated for that exact section or you need a narrower follow-up query.
77+
7678
If you fetch the pieces by hand instead, run **all six** — they are cheap reads:
7779

7880
```bash
@@ -364,6 +366,7 @@ List war rooms
364366
- **`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.
365367
- **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.
366368
- **Use `--fields` to keep list scans compact.** When the goal is to identify matching incidents or collect IDs/numbers/titles, project only the needed columns first, then fetch one target incident with `detail` / `alerts` / `timeline`.
369+
- **Page-sized scans go to files.** For analytics over many incidents, use `--json > /tmp/incidents.json` and run `jq` against the file. Print only the aggregate or selected fields; avoid raw `--limit 100` JSON/toon dumps in chat.
367370
- **`--list` window cap**: `--since`/`--until` window must be < 31 days; `--limit` max 100. Empty result is authoritative — do not widen filters or retry.
368371
- **`merge` is irreversible**: source incidents are absorbed into target permanently. Always list and confirm both IDs before running.
369372
- **`remove --force`** bypasses the interactive confirmation prompt — never pass `--force` unless the user has explicitly said so.

skills/flashduty/reference/insight.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,13 +328,16 @@ Two families with **identical value syntax** but different flag names:
328328

329329
Both families accept: relative duration (`30d`, `24h`), `now`, `+7d`, a date, or Unix seconds — **except** `incident-export`, which takes **epoch seconds only** for `--start-time`/`--end-time` (flag type is int64).
330330

331+
For multi-dimensional reports, fetch once to a temp file, then run every `jq` aggregation against that same file. Do not run the same account-wide list again for each `jq` bucket; if you need status, severity, channel, and responder cuts, one JSON capture plus local aggregations is cheaper and more consistent.
332+
331333
## Gotchas
332334

333335
- **Two time-flag families.** Passing `--since` to an `--start-time` command (or vice-versa) fails with `unknown flag`. See the table above.
334336
- **`incident-export --start-time`/`--end-time` are epoch seconds only**, not relative strings — use `$(date -v-30d +%s)`. All other `--start-time` commands accept `30d`/`now`.
335337
- **`top-alerts --label`** only accepts `check` or `resource`. Any other value (e.g. `integration_name`) returns HTTP 400.
336338
- **Export commands output raw CSV, not JSON.** Redirect to a file; dumping CSV into context burns tokens and is unreadable. No `--limit`/`--page` — exports emit the full filtered set.
337339
- **`insight incidents` and `incident-list` are siblings**, not the same. `incidents` uses `--since`/`--until`, paginates, and is token-light. `incident-list` uses `--start-time`/`--end-time`, adds `--severities`/`--responder-ids`/`--query`/cursor (`--search-after-ctx`), and is the filterable variant.
340+
- **Large `incident-list` pages belong in files.** For any `--limit 100` or broad-window `incident-list`, redirect JSON to `/tmp/*.json`, then print only totals or selected fields. Do not paste page-sized JSON/toon into the transcript.
338341
- **All `insight` commands hit the OLAP backend.** HTTP 500 means the backend is down — report it, do not retry.
339342
- **Empty result is authoritative.** A zero-row response means no matching data for that scope/window — do not widen filters or re-query with shifted keywords.
340343
- **`--aggregate-unit`** (on `account`, `alert-topk-by-label`, `channel`, `responder`, `team` and their exports) splits results into time buckets: `day` / `week` / `month`. When set, the window must span ≥24 h; `day` additionally caps the range at 31 days.

0 commit comments

Comments
 (0)