---
name: oncall-toolkit
description: |
  Oncall workflow assistant for Jira and Pingkai tickets: report, RCA generation,
  filtered Jira or Pingkai issue listing, Jira close flows with preview or direct execution,
  ticket-driven diagnosis routing through related skills, and evaluator-ready case discovery
  for diagnosis benchmark flows such as tikv-slow.
  Use when the user asks to run `oncall-toolkit report ...`,
  `oncall-toolkit RCA {jira-issue-id}`, `oncall-toolkit list ...`,
  `oncall-toolkit analyze {jira-issue-id}`,
  `oncall-toolkit evaluate tikv-slow ...`,
  or `oncall-toolkit close [--preview|--no-preview] {jira-issue-id}` for
  TCOC/GTOC-style Jira tickets or Pingkai `ticket-xxxx` / `TICKET-xxxx` style support tickets.
  In flag-style filter forms, use `--assignee` as the user-facing flag name.
  For Jira-facing actions, prefer `jira-cli` as the primary execution path and use
  direct Jira REST API only as a fallback when CLI output is unavailable or insufficient.
---

# oncall-toolkit

Use this skill for oncall workflows, including diagnosis entry routing and evaluator-oriented case discovery.

Capability layers:
- `discovery`: `list`, `evaluate`
- `diagnosis`: `analyze`
- `reporting`: `report`, `storage-weekly-report`, `RCA`
- `execution`: `close`, `labor`

Use these layers as the default mental model when extending this skill:
- put source discovery, candidate filtering, and evaluator bundle construction under `discovery`
- put issue or link driven troubleshooting under `diagnosis`
- put human-readable summary generation under `reporting`
- put state-changing or workflow-execution operations under `execution`
- when adding new behavior, extend one of these four layers instead of introducing a fifth parallel top-level shape

Supported commands:
- `oncall-toolkit report {issue-id}`
- `oncall-toolkit report [--source=...] [--components=...] [--time_range=...] [--assignee=...]`
- `oncall-toolkit storage-weekly-report [--start-time=...] [--end-time=...]`
- `oncall-toolkit RCA {jira-issue-id}`
- `oncall-toolkit list [--source=...] [--status=...] [--components=...] [--time_range=...] [--assignee=...]`
- `oncall-toolkit analyze {issue-id}`
- `oncall-toolkit evaluate tikv-slow [issue-id...]`
- `oncall-toolkit close {jira-issue-id}`
- `oncall-toolkit labor {issue-id}`

Naming rule:
- `report` is the canonical command name
- do not expose `summary` as the user-facing command
- if `summary` is still mentioned anywhere in legacy context, treat it as `report`

Supported issue input:
- one Jira issue key such as `TCOC-4930`
- one Pingkai ticket alias such as `ticket-8368` or `TICKET-8368` for issue-oriented `report` or `labor`
- multiple Jira issue keys for `report` or `close`, separated by whitespace or comma
- mixed Jira and Pingkai issue ids for explicit `evaluate tikv-slow` input, for example `GTOC-8286 TICKET-8412`

Use English for the final generated summary or RCA unless the user explicitly asks for another language.
For `report`, both the field labels and the generated content must be fully in English, even when the source title, description, or comments are written in another language.
For Jira `report` generation:
- `Phenomenon` must be summarized from the issue `description`, not from comments
- `Root cause`, `Workaround`, and `TODO` must only use comments from `assignee` and `L2 assignee`
- when multiple owner comments exist, prefer the later owner conclusion
- if no explicit owner conclusion exists, do not guess: leave `Root cause` and `TODO` blank, and use `None yet` for `Workaround`
- `Version` should prefer the runtime cluster version resolved by `Cluster ID` through the Cloud Clinic API when the Jira issue provides a cluster id; fall back to Jira `Cluster Version`, then `versions` / `fixVersions`

## Ticket Type Routing

Before executing a mode, classify the provided issue identifier.

Routing rules:
- `TCOC-xxxx` or `GTOC-xxxx` → Jira workflow in this skill
- `ticket-xxxx` or `TICKET-xxxx` in a Pingkai-support context → Pingkai workflow in this skill
- if ambiguous, ask one short clarification question instead of guessing

Mode support by issue type:
- Jira: `report`, `RCA`, `list`, `analyze`, `evaluate`, `close`, `labor`
- Pingkai: `report`, `list`, `analyze`, `evaluate`, `labor`
- Cross-source aggregation: `storage-weekly-report` and discovery-mode `evaluate`

For Pingkai tickets:
- use `scripts/get_pingkai_ticket.py` to fetch one ticket by alias or ID
- prefer environment variables for config: `PINGKAI_API_BASE_URL`, `PINGKAI_TICKET_USERNAME`, `PINGKAI_TICKET_PASSWORD`
- treat file-based `scripts/config.txt` as legacy compatibility only; do not rely on it for new setups
- do not recommend passing Pingkai usernames or passwords as CLI flags unless the user explicitly asks for a legacy fallback
- read ticket body and comments chronologically
- include internal comments by default for Pingkai ticket analysis and report generation
- do not claim Jira-only actions such as transitions, JQL, or RCA template publishing unless the user explicitly asks for a Pingkai-specific equivalent and the workflow supports it

## Command Routing

Parse the first token after `oncall-toolkit` as the mode.

Valid modes:
- `report`
- `storage-weekly-report`
- `RCA`
- `list`
- `analyze`
- `evaluate`
- `close`
- `labor`

Interpretation:
- `report`: either generate a report for a provided issue id, or generate a filtered ticket report set using `report [--source=...] [--components=...] [--time_range=...] [--assignee=...]`
- `storage-weekly-report`: generate the markdown body for the storage weekly report, with fixed sections `PD`, `TXN`, and `TiKV`
- `RCA`: generate an RCA payload for one Jira issue; the script produces structured content and the Feishu plugin performs the actual doc write
- `list`: list Jira issues by default, Pingkai tickets when `--source=pingkai` is used, or aggregate both when `--source=jira,pingkai` or `--source=pingkai,jira` is used
- `analyze`: fetch one Jira or Pingkai ticket, extract the problem statement plus comments, route to relevant diagnosis skills, and invoke `op-clinic-grafana-access` when Clinic or Grafana monitoring evidence is needed
  - `analyze` also supports a direct online Grafana link input, for example `oncall-toolkit analyze http://34.118.198.149:3000/ --from 1775267400 --to 1775269200`
- `evaluate`: build an evaluator-ready case set for one diagnosis profile such as `tikv-slow`; this mode discovers or normalizes candidate incidents, filters them by the requested scope, and returns only ticket-visible initial inputs plus link evidence for downstream replay
- `close`: support `close --preview` and `close --no-preview` for Jira issues, mainly `TCOC-*` and `GTOC-*`; default is preview mode
- `labor`: show Jira worklogs and current-user owner-comment timestamps, or add one Jira worklog entry

If the mode is missing or invalid, ask the user to use one of the supported forms above.

## storage-weekly-report Mode

Use `storage-weekly-report` when the user wants the storage oncall weekly report body generated from Jira plus Pingkai.

### storage-weekly-report Syntax

```text
oncall-toolkit storage-weekly-report
oncall-toolkit storage-weekly-report --start-time "2026-03-27 18:00" --end-time "2026-04-03 18:00"
```

Supported time forms include:
- `YYYY-MM-DD`
- `YYYY-MM-DD HH:MM`
- `YYYY-MM-DD HH:MM:SS`
- ISO 8601 such as `2026-04-03T18:00:00+08:00`

Time-window behavior:
- default with no flags: use the most recent 7 days
- when only `--start-time` is set: use a 7-day window starting from that time
- when only `--end-time` is set: use the 7 days ending at that time
- when both are set: use the explicit `[start, end)` window

### storage-weekly-report Behavior

- render fixed H2 sections in this order:
  - `## PD`
  - `## TXN`
  - `## TiKV`
- for Jira content in each section:
  - search `project in (TCOC, GTOC)` within the requested time window
  - map sections to Jira components:
    - `PD` -> `PD`
    - `TXN` -> `TiDB Transaction`
    - `TiKV` -> `TiKV`
  - render each Jira issue in English
  - keep owner mentions on the same heading line using `L2 assignee` plus Jira `assignee`
- for Pingkai content in each section:
  - search the matching Pingkai module within the same time window
  - include only tickets where `l3_assignee` is non-empty, or `l2_assignee` equals `江红梅`
  - render each Pingkai ticket in English
  - keep owner mentions on the same heading line using `l2_assignee` plus `l3_assignee`
- do not emit `Labels: N/A`
- leave `Root cause` and `TODO` blank when the extracted value is `TBD` or `TBD.`

## analyze Mode

Use `analyze` when the user wants one issue-oriented diagnosis flow starting from a Jira or Pingkai ticket.

### analyze Prerequisite

Before using `oncall-toolkit analyze` for any Clinic link or online Grafana link, always remind the user:

- this flow reuses the local Google Chrome login state on the same machine account
- for Clinic-based analysis, the user must first log in to `https://clinic.pingcap.com/` or `https://clinic.pingcap.com.cn/` in Chrome
- for online Grafana analysis, if the target Grafana is not anonymously readable, the user must first log in to that Grafana in Chrome
- if the user logged in with a non-default Chrome profile, pass the corresponding profile flag through to the underlying analyzer
- if the browser cookie is missing or expired, ask the user to log in again and rerun

`oncall-toolkit analyze` does not use a Local Clinic API key for this path. Clinic and Grafana access are based on reusing browser cookies through `op-clinic-grafana-access`, while the diagnosis and report wording are produced in `oncall-toolkit analyze`.

### analyze Evidence Priority

When `analyze` is used for diagnosis, always apply this evidence order:

- first priority: slow logs, `EXPLAIN ANALYZE` execution detail, statement detail embedded in ticket comments, and extracted raw text attachments
- second priority: Clinic metrics, Clinic bundle logs, and Clinic-derived cluster evidence
- third priority: online Grafana metrics only when no Clinic link is available
- last priority: screenshots or image attachments only when no text logs, Clinic metrics, or raw logs are available

Mandatory rules:

- if slow-log evidence already identifies a suspicious stage such as `prewrite`, `store_batch_wait`, `persist_log`, `commit_log`, or `sync_log`, use that as the first focus for later Clinic analysis
- if a ticket or comment corpus contains multiple distinct incident windows, do not collapse them into one diagnosis; split them into separate per-window analyses first
- if a Clinic link exists, prioritize Clinic evidence over screenshots, but do not let Clinic panels replace already available slow-log stage evidence
- if Clinic `data_status == -1`, `analyze` must wait and poll for up to 10 minutes before deciding the Clinic evidence is unavailable
- if Clinic `data_status` still does not reach `100` after that 10-minute wait window, return a blocking error that says critical evidence is missing and analysis cannot continue
- if Clinic `data_status` is not ready yet for other statuses, wait for `status == 100` and rerun; do not switch to screenshot-based diagnosis just because Clinic metrics are temporarily unavailable
- if no Clinic link exists, then Grafana or screenshots can be used as fallback evidence
- in diagnosis mode, comments are not primary evidence
- build the first-pass diagnosis from the issue or ticket description plus slow-log or execution-detail evidence when present
- after slow-log first-pass diagnosis, use Clinic metrics to confirm scope, onset order, and whether the same bottleneck also appears in `p99`, `p999`, `p99999`, or `max`
- if slow-log evidence proves a long-tail issue but common Clinic quantiles look flat, escalate from `p99` to `p999`, then `p99999`, then `max` instead of concluding that TiKV is healthy
- use comments only as secondary clues, cross-checks, or follow-up hints; never treat comments as the final conclusion by themselves

### analyze Syntax

```text
oncall-toolkit analyze GTOC-1234
oncall-toolkit analyze ticket-8368
oncall-toolkit analyze --online-grafana http://34.118.198.149:3000/ --from 1775267400 --to 1775269200
oncall-toolkit analyze GTOC-1234 --online-grafana http://34.118.198.149:3000/ --from 1775267400 --to 1775269200
oncall-toolkit analyze GTOC-1234 --clinic-link "https://clinic.pingcap.com.cn/portal/#/orgs/1020/clusters/7506061236641706840?from=1775007600&to=1775011200"
oncall-toolkit analyze GTOC-1234 --clinic-link "https://clinic.pingcap.com.cn/portal/#/orgs/1020/clusters/7506061236641706840" --from 1775007600 --to 1775011200
```

Optional flags:

```text
oncall-toolkit analyze GTOC-1234 --skip-clinic
oncall-toolkit analyze GTOC-1234 --skip-clinic-download
oncall-toolkit analyze GTOC-1234 --skip-attachments
oncall-toolkit analyze ticket-8368 --max-clinic-links 2
oncall-toolkit analyze ticket-8368 --clinic-download-root ~/Downloads
oncall-toolkit analyze ticket-8368 --download-attachments
oncall-toolkit analyze GTOC-1234 --json
oncall-toolkit analyze --online-grafana http://34.118.198.149:3000/ --from 1775267400 --to 1775269200
oncall-toolkit analyze --online-grafana http://34.118.198.149:3000/ --grafana-dashboard-uid eDbRZpnWk --grafana-datasource-id 1
oncall-toolkit analyze GTOC-1234 --online-grafana http://34.118.198.149:3000/
oncall-toolkit analyze GTOC-1234 --clinic-link "https://clinic.pingcap.com.cn/portal/#/orgs/1020/clusters/7506061236641706840"
```

### analyze Behavior

- fetch exactly one Jira issue or one Pingkai ticket
- or accept one explicit `--online-grafana` link as the primary analysis target
- or accept one explicit `--clinic-link` as the primary analysis target
- when both a ticket ref and `--online-grafana` / `--clinic-link` are provided, use the ticket as the problem context and the explicit monitoring link as the preferred evidence source
- when `--clinic-link` does not already include `from/to`, use `--from` and `--to` to fill the Clinic window
- extract the ticket title and description summary first
- delay comment usage until after the description plus Clinic or log evidence has been analyzed
- extract Clinic links from summary, description, and comments
- extract Grafana links from summary, description, and comments
- extract Clinic bundle download links from summary, description, and comments when present
- extract ticket attachment references:
  - Pingkai: inline `/api/v1/files/download/...` file links found in description or comment HTML
  - Jira: `fields.attachment[]`
- scan the full comment corpus for:
  - multiple distinct time windows
  - slow-log excerpts or execution-detail snippets
  - reporter or customer follow-up evidence
  - analysis-stage pivots that separate one window from another
- if multiple time windows or multiple distinct symptom families are found, create separate analysis tracks per window before writing any unified diagnosis summary
- for each analysis track, prefer:
  - slow log or execution detail first
  - Clinic metrics second
  - comments and screenshots only as supporting context
- recommend relevant diagnosis skills from the diagnosis skill set based on the extracted problem text
- when Clinic links are found and Clinic analysis is not skipped, invoke `op-clinic-grafana-access` to collect normalized monitoring evidence
- when Grafana links are found and Grafana analysis is not skipped, invoke the shared online Grafana access flow in `op-clinic-grafana-access`
- when Clinic links are found, check Clinic `data_status` for the same window and wait until `status == 100` before treating empty metrics as evidence
- if Clinic `data_status == -1`, keep polling for up to 10 minutes during analysis
- if Clinic `data_status` still does not reach `100` after the 10-minute wait window, return a blocking error instead of downgrading to ticket comments or screenshots
- if Clinic `data_status` is not ready yet for other statuses, stop at a pending Clinic status and tell the user to wait for Clinic readiness; do not replace Clinic with screenshots unless no Clinic evidence exists at all
- when Clinic bundle download links are found and bundle download is not skipped, invoke `op-clinic-grafana-access/scripts/download_bundle.py` to download and extract the bundle locally, and gate the download on Clinic `data_status == 100` whenever the matching Clinic portal link is known
- prefer `op-clinic-grafana-access/scripts/dispatch_access.py` as the only integration entrypoint from `oncall-toolkit`; do not couple `oncall-toolkit` to multiple internal access scripts when one dispatcher route can express the request
- when bundle download is triggered from `analyze`, store it under `~/Downloads/{issue-id}/` by default, for example `~/Downloads/TICKET-8412/` or `~/Downloads/GTOC-1234/`
- when logs / profile / trace / config style evidence is needed and attachments are present, download attachments into `~/Downloads/{issue-id}/attachments/`
- during attachment download for `analyze`, ignore common image attachments by default, such as `png`, `jpg`, `jpeg`, `gif`, `webp`, `bmp`, `tiff`, and `svg`; keep the focus on logs, profiles, traces, configs, and other text or binary evidence that can support diagnosis
- when slow-log files are present, treat them as first-class diagnosis inputs rather than passive attachments
- when slow-log files point to one specific TiKV stage, Clinic analysis should focus on the corresponding metric family and the same wall-clock window
- when cluster configuration is needed, prefer the Clinic bundle path over screenshots or comments:
  - download and extract the Clinic bundle
  - read `meta.yaml`
  - use `topology.server_configs` as the cluster-wide default configuration source
  - use per-instance `config` blocks only as override evidence for specific nodes
- treat screenshots and image attachments as evidence files to retain, but only analyze them when Clinic evidence is absent; do not over-interpret them as text logs
- when the Clinic data supports it, prefer a clinic-driven RCA structure with:
  - incident scope
  - root-cause hypothesis
  - primary evidence
  - counterevidence / what is not supported
  - recommended actions
- for both Clinic and Grafana analysis, render a cluster summary first, then the diagnosis judgement
- treat ticket comments, owner comments, and ticket-side conclusions as clues only, not as the final diagnosis
- do not let comments override Clinic metrics, raw logs, or config evidence unless the monitoring evidence is missing
- when Clinic analysis is available, use Clinic-derived findings as the primary diagnosis evidence in the final report
- when only Grafana analysis is available, use the Grafana-derived cluster summary and metric judgement as the primary monitoring evidence
- when multiple analysis tracks exist, the output must explicitly separate them by time window or symptom family instead of collapsing them into one blended conclusion
- when one window is TiKV-side and another is TiDB-side or application-side, say that explicitly and keep the recommendations separate
- by default, render a user-readable markdown diagnosis report
- when `--json` is used, return a structured diagnosis bundle containing:
  - `problem_statement`
  - `ticket_context`
  - `diagnosis.recommended_skills`
  - `diagnosis.routing_reasons`
  - `diagnosis.clinic_analysis`
  - `diagnosis.clinic_bundle_analysis`
  - `diagnosis.attachment_analysis`

### analyze Routing Rules

- always include `tidb-perf-diagnosis` as the default general diagnosis skill
- add `op-clinic-grafana-access` when a Clinic link is found in the ticket context
- add `op-clinic-grafana-access` when a Grafana link is found in the ticket context
- add `op-clinic-grafana-access` when only a Clinic bundle download link is found in the ticket context
- add more specialized skills only when the ticket text strongly suggests them:
  - `tikv-slow-diagnosis` for leader drop, re-election, IO jitter, EBS / disk jitter, store-local tail latency, TiKV network issue, `tikvRPC`, `unreachable`, `RPC Layer Latency`, `ServerIsBusy`, or flow-control keywords
  - `tikv-fast-tune` for TiKV / raft / gRPC / commit-log style latency keywords
  - `tikv-memory` for OOM / memory-pressure keywords
  - `tikv-recovery` for recovery / replica-loss keywords
  - `transaction-oncall-handbook` for transaction / lock keywords
  - `ticdc-health-inspection` for TiCDC / changefeed keywords

### analyze Output Rules

- the default markdown output should read like a user-facing diagnosis report, not a raw routing dump
- when Clinic evidence is strong enough, the markdown should look closer to a final RCA than to a generic triage note
- when the case matches the TiKV network-issue workflow, the markdown should explicitly state:
  - affected hop
  - whether the issue is interruption, jitter, or bandwidth saturation
  - which transport metrics prove the conclusion
  - what evidence rules out TiKV-local append/apply bottlenecks or gRPC false positives
- when the case matches the TiKV flow-control workflow, the markdown should explicitly state:
  - which exact `ServerIsBusy` or throttling mechanism fired
  - whether it is write-side or read-side
  - which signal proves the threshold crossing or effective threshold crossing
  - which lower-layer bottleneck triggered the protection
  - that threshold tuning alone is only symptom mitigation
- when Clinic bundle extraction succeeds, surface the local archive path, extracted directory, meta.yaml summary, and which component logs were actually present
- when Clinic bundle extraction succeeds and configuration is relevant, surface the `meta.yaml` configuration summary as part of the diagnosis evidence
- when attachment download succeeds, surface the local attachment path and any lightweight text-log clues that were extracted
- the diagnosis bundle should still remain machine-readable when `--json` is used
- keep the extracted ticket context concise enough for downstream skill routing
- when multiple Clinic links are found, prefer the most recent comment-provided links first

## evaluate Mode

Use `evaluate` when the user wants an evaluator-ready case set instead of one live incident diagnosis.

Current supported profile:
- `tikv-slow`

### evaluate Syntax

```text
oncall-toolkit evaluate tikv-slow
oncall-toolkit evaluate tikv-slow GTOC-8286 TICKET-8412
oncall-toolkit evaluate tikv-slow --source=jira,pingkai --time_range=30d --assignee="jianghongmei@pingcap.com" --sample-size=5
oncall-toolkit evaluate tikv-slow --clinic-only --ignore-grafana-only
oncall-toolkit evaluate tikv-slow --json
```

### evaluate Behavior

- keep `analyze` and `evaluate` separate:
  - `analyze` is for one incident diagnosis
  - `evaluate` is for multi-case benchmark preparation
- for `tikv-slow`, the command should only build the evaluator-ready input bundle:
  - candidate discovery
  - source filtering
  - title / description normalization
  - Clinic / Grafana link extraction from initial ticket-visible fields
  - inclusion / exclusion reasons
- do not use owner conclusions, later comments, or retrospective evidence as replay input in `evaluate`
- if explicit case ids are provided, normalize exactly those ids first and then apply requested filters such as `--clinic-only`
- default discovery scope for `tikv-slow`:
  - source: `jira,pingkai`
  - time range: `30d`
  - assignee: `江红梅`
  - sample size: `5`
  - require a Clinic link in the initial ticket body or Jira description
  - exclude cases with no Clinic link, including Grafana-only cases
- for Jira discovery in `tikv-slow`, limit first-pass discovery to `GTOC` + `TiKV`
- `--clinic-only` means the initial ticket body or Jira description must contain a Clinic link; this is enabled by default for `tikv-slow`
- `--ignore-grafana-only` means exclude cases where the initial ticket body has Grafana evidence but no Clinic link; this is enabled by default for `tikv-slow`
- the resulting bundle should be suitable for the replay rules below

### evaluate tikv-slow Methodology

Use this section when the goal is to evolve `tikv-slow-diagnosis` through real-case review rather than diagnose one incident directly.

Ownership rule:
- `oncall-toolkit` is the execution entry point for `evaluate tikv-slow`
- the evaluator definition layer belongs to
  `skills/diagnosis/tikv-slow-diagnosis/evaluation/`
- do not let `oncall-toolkit` become the long-term source of truth for
  `tikv-slow` replay policy

Supported request shapes:
- if the user provides explicit incident ids, evaluate exactly those ids and skip discovery
- if the user asks for recent-case evaluation without ids, use the default scope below
- if the user asks about one live incident only, do not use this evaluator path; use `tikv-slow-diagnosis` or `oncall-toolkit analyze` instead

Default scope:
- time range: last `30d`
- sources: `Pingkai ticket + Jira GTOC`
- owner filter: `江红梅` on the Pingkai side and the current Jira-side stable owner identifiers on the Jira side
- symptom focus: latency / duration / timeout style slow incidents
- sample size: `5`
- require a Clinic link in the initial ticket body or Jira description
- ignore issues with no Clinic link and issues that are Grafana-only
- do not silently expand this scope to `TCOC`

Hard rules:
1. `human_conclusion_unclear` is a formal output state. If the final owner conclusion is not explicit enough, mark the case that way and do not treat it as a positive learning example.
2. This evaluator produces `review / compare / suggestions` only. It must not directly modify `tikv-slow-diagnosis`.
3. Keep two layers only:
   - definition layer
   - execution layer
   Do not add a third layer or a parallel structure in the first version.
4. The definition layer is the source of truth. The execution layer may only consume fields and categories defined there and must not invent new judgement dimensions during replay.
5. Improvement suggestions must be tied back to concrete case gaps. Do not propose broad changes that are not grounded in observed mismatches.
6. Replay must use only ticket-visible initial inputs. Do not leak the final owner conclusion, later comments, or retrospective evidence into the replay step.
7. Use only repository-real execution paths:
   - candidate discovery: `oncall-toolkit`
   - Jira retrieval: `jira-cli` first, Jira REST API fallback only when needed
   - Pingkai retrieval: the same Pingkai path already used by `oncall-toolkit`
   - Clinic or Grafana evidence reuse: `op-clinic-grafana-access` when those links already exist in the initial ticket body

Definition layer:

Allowed `mismatch_type` values:
- `wrong_routing`
- `wrong_scope`
- `wrong_path`
- `stopped_too_early`
- `overclaimed_root_cause`
- `missing_known_issue`
- `missing_required_evidence`

Do not add additional mismatch types in the first version.

Every reviewed case should normalize to:

```json
{
  "incident_id": "GTOC-1234 or TICKET-8412",
  "source": "jira_gtoc or pingkai",
  "initial_symptom": "",
  "canonical_incident": {
    "title": "",
    "description_text": "",
    "cluster_id": "",
    "clinic_links": [],
    "grafana_links": [],
    "time_window": "",
    "l2_assignee": "",
    "assignee_or_l3": ""
  },
  "replay_inputs": {
    "used_fields": [],
    "excluded_fields": [],
    "notes": ""
  },
  "skill_conclusion": {
    "entry_mode": "",
    "activated_subcapabilities": [],
    "reference_files_consulted": [],
    "is_tikv_side": "",
    "scope": "",
    "path": "",
    "earliest_proven_bottleneck": "",
    "key_evidence": [],
    "missing_evidence": [],
    "uncertainty_statement": "",
    "provisional_conclusion": ""
  },
  "final_human_conclusion": {
    "status": "clear or human_conclusion_unclear",
    "owner_final_comment": "",
    "structured_conclusion": "",
    "evidence_references": []
  },
  "match_level": "matched | partially_matched | mismatched | human_conclusion_unclear",
  "mismatch_type": [],
  "comparison_notes": [],
  "improvement_suggestions": []
}
```

`match_level` rubric:
- `matched`: replay and final human conclusion agree on the primary path and main bottleneck layer
- `partially_matched`: replay gets the main direction mostly right, but bounded mismatch types still apply
- `mismatched`: replay is directionally wrong enough that the resulting improvement action would differ materially
- `human_conclusion_unclear`: final owner conclusion is too exploratory, fragmented, or implicit to use as a comparison anchor

Do not use `matched` when `overclaimed_root_cause` applies.

Execution layer:

Entry resolution:
- `explicit_case_set`
- `default_benchmark`
- `custom_scope_benchmark`

Candidate selection rules:
- Jira source is limited to `GTOC`
- Pingkai source is limited to tickets where the effective owner filter matches the benchmark owner scope
- ignore `TCOC`
- prioritize summary or description signals such as `slow`, `latency`, `duration`, `timeout`, `p99`, `p999`, `kv duration`, `grpc duration`, `cop slow`, `batchget`, `prewrite`, `commit`
- treat these as candidate filters, not final diagnosis

Candidate exclusion rules:
- no meaningful summary and no meaningful description
- no owner-side final conclusion can be extracted
- missing Clinic link in the initial ticket body or Jira description
- Grafana-only initial evidence without a Clinic link
- clearly not a slow / latency style case after reading summary and description
- dominated by a different primary domain such as recovery, TiCDC health, or transaction correctness

Replay boundary:

Allowed replay inputs:
- ticket summary
- ticket description
- cluster id if present in the initial ticket body
- Clinic links or Grafana links if present in the initial ticket body
- incident time window if present in the initial ticket body

Disallowed replay inputs:
- all comments
- owner-side conclusions
- later-added evidence links that appear only in comments
- retrospective or manually curated RCA summaries

Replay checkpoints for `skill_conclusion`:
- `entry_mode`: one of `not_tikv_slow_diagnosis`, `generic_tikv_slow`, `leader_drop`, `io_jitter`, `network_issues`, `flow_control`, `snapshot_backlog`
- `activated_subcapabilities`
- `reference_files_consulted`
- `uncertainty_statement`

Allowed `reference_files_consulted` values include:
- `references/cross-cutting.md`
- `references/read-path.md`
- `references/write-path.md`
- `references/leader-drop.md`
- `references/io-jitter.md`
- `references/network-issues.md`
- `references/flow-control.md`
- `references/snapshot-backlog.md`

Human conclusion extraction rules:
- Jira: use `assignee` comments and `L2 assignee` comments
- Pingkai: use `l2_assignee` comments and `l3_assignee` comments
- prefer the final `L2 assignee` conclusion when it exists
- otherwise fall back to the final `assignee` or `l3` owner-side conclusion
- only use the final explicit owner-side conclusion
- if the latest owner-side comments are still exploratory, set `status = human_conclusion_unclear`
- do not merge multiple speculative comments into one fake conclusion
- do not use non-owner comments as the final human conclusion

Comparison priority:
1. routing correctness
2. whether the correct sub-capability was activated
3. whether the correct reference files would have been consulted
4. TiKV-side versus non-TiKV-side split
5. read versus write versus both
6. scope classification
7. whether the replay stopped too early
8. whether the replay overclaimed certainty

Suggestion guardrails:
- every improvement suggestion must be traceable to one or more case mismatches
- allowed buckets:
  - `workflow`
  - `knowledge`
  - `output`
- do not suggest ungrounded large refactors
- do not suggest new diagnosis branches unless reviewed cases motivate them
- do not auto-edit the main skill in the same run

Improvement input to `tikv-slow-diagnosis`:

This is the fourth object in the current review frame. It is the bridge from
`evaluate tikv-slow` review output into later edits of `tikv-slow-diagnosis`.

Do not introduce a separate fifth parallel structure for this object. In the
first version, it must be expressed through the existing aggregated sections:
- `workflow_improvements[]`
- `knowledge_improvements[]`
- `output_improvements[]`
- `do_not_change_yet[]`

Every aggregated improvement item should normalize to:

```json
{
  "bucket": "workflow | knowledge | output | do_not_change_yet",
  "title": "",
  "problem_statement": "",
  "proposed_skill_change": "",
  "target_surface": "entry_routing | scope_or_path_logic | required_evidence | reference_activation | output_wording_and_uncertainty",
  "source_case_ids": [],
  "source_mismatch_types": [],
  "evidence_strength": "repeated_clear_cases | single_case_strong | insufficient",
  "merge_readiness": "eligible_now | review_only | do_not_change_yet",
  "rationale": ""
}
```

Improvement-input eligibility rules:
- classify every aggregated proposal into exactly one of:
  - `eligible_now`
  - `review_only`
  - `do_not_change_yet`
- do not introduce a fourth promotion state in the first version
- only promote cases whose `final_human_conclusion.status = clear`
- every item must trace back to at least one reviewed case and one explicit `mismatch_type`
- only propose changes that fit the current `tikv-slow-diagnosis` surfaces:
  - entry routing
  - scope / path split
  - required evidence and stopping discipline
  - reference-file activation
  - output wording and uncertainty handling
- use `merge_readiness = eligible_now` only when all of these hold:
  - `final_human_conclusion.status = clear`
  - the case is not labeled `human_conclusion_unclear`
  - the proposal is traceable to one or more explicit `mismatch_type` values
  - the proposal stays within the current `tikv-slow-diagnosis` shape and does not require a new fifth parallel object
  - the proposal is supported by at least `2` clear reviewed cases, or one clear case shows a materially wrong outcome caused by `missing_required_evidence` or `overclaimed_root_cause`
- use `merge_readiness = review_only` when:
  - the gap is plausible but not yet stable enough to change the main skill
  - the proposal is supported by only `1` case and is not an obviously reusable fix
  - the proposal would require a new diagnosis branch or a broader structural refactor
  - the proposal depends on evidence outside the replay boundary
  - the compare wording is still informative, but not stable enough to harden into the main skill
- use `merge_readiness = do_not_change_yet` when:
  - `match_level = human_conclusion_unclear`
  - the dominant problem is `missing_required_evidence`
  - the case mainly exposes an evidence-availability gap rather than a main-skill judgement gap
  - the final owner conclusion is too implicit, fragmented, or speculative to support a durable skill update
- do not use excluded cases, Grafana-only cases, or comment-only evidence as improvement input

Improvement-input merge guardrails:
- `eligible_now`: safe to feed into the next `tikv-slow-diagnosis` edit proposal
- `review_only`: useful signal, but not yet stable enough to change the main diagnosis skill
- `do_not_change_yet`: explicitly track the idea, but do not let it influence the main skill now
- when in doubt between `eligible_now` and `review_only`, prefer `review_only`

Aggregation rules:
- aggregate repeated gaps into:
  - `high_frequency_gaps[]`
  - `workflow_improvements[]`
  - `knowledge_improvements[]`
  - `output_improvements[]`
  - `do_not_change_yet[]`
- only treat a gap as high-frequency when it appears in at least `2` reviewed cases
- if a proposal is supported by only `1` case, keep it case-local unless the evidence is unusually strong
- if cases are too heterogeneous, prefer `do_not_change_yet[]`
- do not elevate anything from a `human_conclusion_unclear` case into `high_frequency_gaps[]`
- prioritize reducing `wrong_routing`, `missing_required_evidence`, and `overclaimed_root_cause`
- the fourth object, `improvement input to tikv-slow-diagnosis`, must be derived from these aggregated sections rather than invented as a parallel output path

Workflow phases:
1. resolve entry mode
2. discover or confirm candidate cases
3. exclude unusable cases and record `sample_summary.excluded_cases[]`
4. build canonical incidents
5. run replay using only initial ticket-visible inputs
6. extract the final owner conclusion
7. compare and assign `match_level`, `mismatch_type`, and case-level suggestions
8. aggregate repeated gaps
9. recommend next edits without auto-editing `tikv-slow-diagnosis` unless the user explicitly asks

Stop early when:
- fewer than `2` usable cases remain after exclusion in benchmark mode
- the requested scope cannot produce Jira or Pingkai candidates
- owner-side comments are inaccessible for most sampled cases
- the ticket body does not contain enough initial inputs for meaningful replay

When stopping early, still return:
- attempted scope
- discovered candidates
- exclusion reasons
- what additional access or data would unblock the evaluation

Expected output sections:
- `sample_summary`
- `case_reviews[]`
- `high_frequency_gaps[]`
- `workflow_improvements[]`
- `knowledge_improvements[]`
- `output_improvements[]`
- `do_not_change_yet[]`

Interpret these sections as follows:
- the first three objects are:
  - `evaluate bundle`
  - `replay input boundary`
  - `comparison result`
- the fourth object, `improvement input to tikv-slow-diagnosis`, is represented by:
  - `workflow_improvements[]`
  - `knowledge_improvements[]`
  - `output_improvements[]`
  - `do_not_change_yet[]`

## labor Mode

Use `labor` when the user wants to inspect or add labor / worklog records for one issue.

### labor Syntax

Show mode:

```text
oncall-toolkit labor {jira_issue_id} show
oncall-toolkit labor ticket-{alias} show
```

Add mode:

```text
oncall-toolkit labor {jira_issue_id} {duration} [--time "YYYY-MM-DD HH:MM:SS"] [-m "..."]
oncall-toolkit labor ticket-{alias} {duration} [--time "YYYY-MM-DD"] [-m "..."]
```

### labor Scope

- Jira issues, mainly `TCOC-*` and `GTOC-*`
- Pingkai tickets are supported for show and add mode
- Pingkai labor accepts only `ticket-xxxx` or `TICKET-xxxx`
- do not route plain numeric aliases such as `8406` into Pingkai `labor`
- do not route 32-char ticket ids into Pingkai `labor`

### labor Show Behavior

For `oncall-toolkit labor {jira_issue_id} show`:
- if `TEMPO_API_TOKEN` is available, prefer Tempo worklog lookup so creator and created time align more closely with Tempo-backed UI
- optional `TEMPO_API_BASE_URL` may override the default `https://api.tempo.io`
- optional `ATLASSIAN_ACCOUNT_ID`, `JIRA_ACCOUNT_ID`, `ATLASSIAN_EMAIL`, or `JIRA_EMAIL` can improve current-user matching for Tempo-backed worklogs
- if Tempo is unavailable, fall back to Jira worklog data, noting Jira may expose the author as `Timesheets by Tempo - Jira Time Tracking`
- fetch the Jira issue
- show only Jira worklog records that belong to the current Jira login user
- show owner-comment timestamps only for the current Jira login user
- when matching the current Jira login user, allow email / display-name alias resolution when needed

Output shape:

```text
### Labor {issue_id} - {title}
- Worklogs:
- started={started} | spent={timeSpent} | author={author} | created_at={created_at_local} | comment={comment}
- Owner comments:
- {created} | owner={author} | comment={summary}
### Suggested labor add
- {suggested_time} | duration={duration} | basis={comment_count} comments / {char_count} chars | command={labor_add_command}
```

If no worklog exists, use `No labor records found.`
If no current-user owner comment exists, use `No owner comments found for current Jira user.`
If no additional labor entry is suggested after comparing comments with existing labor records, use `No labor add suggestions.`

Jira worklog filtering rule:
- do not display other users' worklogs in `labor ... show`
- when using `jira-cli` first or direct script integration, read the issue worklogs and filter them down to the current Jira login user before rendering
- when `TEMPO_API_TOKEN` is configured, resolve each Jira worklog through Tempo `GET /4/worklogs/jira/{jiraWorklogId}` first so `author` and `createdAt` align more closely with Tempo-backed UI
- when using `jira-cli` fallback, keep the same current-user-only behavior in the final output

Tempo alignment note:
- Jira issue `fields.worklog` is the current worklog list, not the same view as Tempo's Worklogs activity tab
- the Tempo-backed Worklogs tab may show audit-style creation events, including creator and created time, that do not map 1:1 to the final Jira worklog list
- if exact Tempo audit alignment is required, use Tempo audit APIs in addition to current worklog APIs

For `oncall-toolkit labor ticket-{alias} show`:
- resolve the Pingkai ticket first so the request uses the canonical `ticket_id`
- fetch labor records through `GET /api/v1/working-time?ticket_id=...`
- show Pingkai working-time records in a worklog-like list
- show owner-comment timestamps only for the current Pingkai login user
- when matching the current Pingkai login user, allow id / username / email / full-name alias resolution
- include internal comments when building the current-user comment list
- if no working-time record exists, use `No labor records found.`
- if no current-user owner comment exists, use `No owner comments found for current Pingkai user.`
- if no additional labor entry is suggested after comparing comments with existing labor records, use `No labor add suggestions.`

### labor Suggestion Rules

When running `oncall-toolkit labor ... show`, also produce `Suggested labor add` lines.

Suggestion inputs:
- only use owner comments for the current login user
- group comments by local calendar date
- compare against existing labor records from the current login user on the same date

Suggestion heuristics:
- use the earliest owner-comment timestamp on that date as the suggestion anchor time
- estimate suggested duration from comment volume and content size
- larger comments, multi-line comments, and comments containing logs, commands, SQL, errors, monitoring notes, or troubleshooting details should increase the suggested duration
- round suggested duration to `0.5h` increments
- cap one date's estimated duration to a reasonable per-day ceiling rather than growing without bound

Suggestion output rules:
- for Jira, the suggested command should include a datetime `--time "YYYY-MM-DD HH:MM:SS"`
- for Pingkai, the suggested command should include the date form `--time "YYYY-MM-DD"` because Pingkai labor uses a date field
- if the user already logged labor on that date, suggest only the remaining delta between the estimated duration and the existing logged duration
- if the existing logged duration is already greater than or equal to the estimate, do not emit an add suggestion for that date

### labor Add Behavior

For `oncall-toolkit labor {jira_issue_id} {duration} [--time ...]`:
- add one Jira worklog record through `jira issue worklog add`
- `--time` is optional
- if `--time` is omitted, use Jira CLI default start time behavior
- if `--time` is provided with date only, normalize it to a valid datetime
- use timezone `Asia/Shanghai` when a time is explicitly provided
- support `-m` or `--comment` to set the worklog comment
- default comment can be `time-tracking` when no comment is provided

For `oncall-toolkit labor ticket-{alias} {duration} [--time ...]`:
- add one Pingkai working-time record through `POST /api/v1/working-time`
- resolve the ticket first through Pingkai API so the payload uses the canonical `ticket_id`
- `--time` is optional
- if `--time` is omitted, use today's date in `YYYY-MM-DD`
- if `--time` contains a datetime, normalize it to the date portion because Pingkai working-time uses a date field
- support optional `-m` or `--comment`
- support `--dry-run` to print the computed request without writing to Pingkai

Accepted duration examples:
- `45m`
- `4h`
- `1.5h`
- `1h30m`

### labor Execution Path

For Jira worklog behavior, keep both execution paths:
- first try `jira-cli`
- if `jira-cli` is unavailable or cannot satisfy the required worklog behavior, fall back to direct Jira REST API
- keep the final behavior and output semantics aligned across both paths

## Shared Jira Workflow

For every Jira-related mode, prefer this execution order:

1. Use `jira-cli` as the default Jira path.
2. If `jira-cli` is unavailable, not reachable, or lacks the exact capability needed for the current step, fall back to direct Jira REST API.
3. Keep the final behavior and output semantics consistent across the available paths.

Execution policy:
- `list`: use `jira-cli` JQL search by default; use Jira REST API search as the fallback
- `report`: use `jira-cli` for Jira search and issue fetch by default; use Jira REST API only as fallback
- `labor`: use `jira-cli` for Jira worklog read or write by default when it can preserve the required behavior; otherwise use Jira REST API
- `RCA`: use `jira-cli` for Jira issue fetch by default; use Jira REST API only as fallback
- `close`: use `jira-cli` for transition metadata, comments, and transitions by default when available; use Jira REST API as fallback

For issue-oriented modes (`report`, `RCA`, `close`), fetch the Jira issue with enough fields to include:
   - `report`
   - `description`
   - `priority`
   - `status`
   - `assignee`
   - `comment`
   - `components`
   - `versions`
   - `fixVersions`
   - `customfield_10149` for TCOC cluster ID when present
3. Prefer `expand=renderedFields` or another representation that preserves long comments.
4. Preserve comment order as `created` ascending.
5. Convert Jira content into plain text carefully:
   - preserve paragraphs and lists
   - preserve `hardBreak` as newline
   - ignore media nodes for summary text extraction
6. For `TCOC-*` issues, try to enrich cluster version from Clinic using `customfield_10149`.
   - if Clinic returns a runtime version, use it
   - otherwise fall back to Jira versions or `N/A`

## list Mode

Use `list` when the user wants a filtered Jira issue list rendered in a compact one-line style that is intentionally different from `report`.

### list Syntax

```text
oncall-toolkit list [--source=jira] [--status=...] [--components=...] [--time_range=...] [--assignee=...]
oncall-toolkit list --source=pingkai [--components=...] [--time_range=...] [--assignee=...] [--page_size=...]
oncall-toolkit list --source=pingkai,jira [--status=...] [--components=...] [--time_range=...] [--assignee=...] [--page_size=...]
```

### list Flags

`--source`:
- accepted: `jira`, `pingkai`, `jira,pingkai`, `pingkai,jira`
- default: `jira`
- `jira,pingkai` and `pingkai,jira` mean aggregate mode across both sources

`--status`:
- accepted: `all`, `unresolved`, `resolved`, `done`, `open`
- default: `all`
- Pingkai mode currently supports `all` only

`--components`:
- accepted examples: `tikv`, `tidb`, `pd`, `ticdc`, `tikv,tidb`
- default: `tikv,tidb,pd`
- Jira: matches Jira `components`
- Pingkai: matches `module_name`; use `--components` instead of `--module`

`--time_range`:
- Jira accepted examples: `1w`, `7d`, `2w`, `30d`, `this_week`
- Pingkai accepted examples: `10d`, `1w`, `2w`, `24h`, `this_week`, `all`
- default: `1w`

`--assignee`:
- accepted for Jira: omitted (`currentUser()`), Jira email, English Jira display name, Jira account id, or a comma-separated list of those values
- do not use Chinese names in Jira JQL; if a Chinese self-name is passed, treat it only as a hint to resolve the current Jira login user's stable aliases
- Jira default: `currentUser()`
- Pingkai default: no owner filter; if provided, match `l2_assignee_name` or `l3_assignee_name`; comma-separated values are allowed

`--page_size`:
- Pingkai-only
- default: `100`
- use values `1` to `100`

### list Filter Semantics

Default scope:
- Jira projects: `TCOC`, `GTOC`
- status: `all`
- components: `tikv,tidb,pd`
- time range: last `1w`
- target assignee: `currentUser()`
- source: `jira`

Pingkai default scope:
- order by `created_at DESC`
- fetch paginated `/api/v1/tickets`
- continue page-by-page until the oldest ticket in the current page is older than the requested `--time_range`
- no owner filter unless `--assignee` is provided

Aggregate source mode:
- when `--source` contains both `jira` and `pingkai`, query both sources independently
- apply the same `--components`, `--time_range`, and `--assignee` semantics to each source using that source's mapping rules
- merge the matched Jira issues and Pingkai tickets into one result set
- sort the combined output by creation time descending

Status mapping:
- `all` → no status-category restriction
- `unresolved` → `statusCategory != Done`
- `resolved` → prefer an explicit resolved-state filter when available in Jira
- `done` → `statusCategory = Done`
- `open` → prefer the project's open-state statuses; if unavailable, fall back to not-done statuses that exclude resolved/done states

Components mapping:
- `all` means no Jira component restriction
- otherwise split on commas and match Jira `components` by display name
- if a component token is ambiguous, ask one short clarification question before querying

Time range mapping:
- `1w` or `7d` → `created >= -7d`
- `2w` → `created >= -14d`
- `30d` → `created >= -30d`
- `this_week` → `created >= startOfWeek()`
- if the user gives another clear relative range, convert it to the nearest JQL equivalent

Pingkai time-range mapping:
- request `GET /api/v1/tickets?pagination.page=...&pagination.pageSize=...&order_by=created_at&order_direction=desc`
- compare each ticket's `created_at` against the parsed time-range cutoff
- stop scanning further pages as soon as one page crosses the cutoff, because the server response is sorted descending by creation time

Assignee mapping:
- if `--assignee` is omitted, use `currentUser()`
- when filtering by `--assignee`, support one or more comma-separated assignees, search both `assignee` and `L2 assignee`, then merge the results
- if the same issue matches both fields, de-duplicate it in the final result
- for Jira, prefer stable identifiers such as email, English display name, or account id; do not emit Chinese names into Jira JQL
- if the user provides a Jira display name or email, resolve it to an account ID first when needed by the execution path

Pingkai assignee mapping:
- `--assignee` matches `l2_assignee_name` or `l3_assignee_name`
- if either `l2_assignee_name` or `l3_assignee_name` matches, include the ticket
- matching is case-insensitive and accepts comma-separated values

### list Query Rules

Use `jira-cli` as the default Jira search path, with fallback to direct Jira REST API only when CLI output is unavailable or insufficient for the exact query behavior.

Base filter shape:
- `project in (TCOC, GTOC)`
- optional component clause when `components_list != all`
- one time-range clause
- one owner clause per requested assignee, each expanded as `(assignee = owner OR "L2 assignee" = owner)`
- optional status clause
- sort by `created DESC`

If a single JQL query is not reliable for both owner fields:
- it is acceptable to run two Jira queries, one for `assignee` and one for `L2 assignee`
- merge the two result sets client-side
- de-duplicate by issue key before rendering output

For Pingkai:
- use the tickets list endpoint directly rather than JQL
- filter client-side by `module_name`, `l2_assignee_name`, and `l3_assignee_name`
- keep the final output sorted by `created_at DESC`

For aggregate mode:
- query Jira and Pingkai independently
- merge the two result sets client-side
- sort the final combined output by creation time descending

### list Output

For each matched issue, output one line in a compact list style that is intentionally different from `report`:

```text
- [{issue_id}](https://tidb.atlassian.net/browse/{issue_id}) | {title} | {created_date} | reporter={reporter} | assignee={assignee} | l2_assignee={l2_assignee} | labels={labels}
```

Rules:
- sort by created time descending unless the user asks otherwise
- keep exactly one line per issue
- include only: title, created time, reporter, assignee, `L2 assignee`, and `labels`
- render `labels` from the real Jira `labels` field
- when a field is empty, render it as `N/A`
- if no issues match, say `No Jira issues found for the requested filters.`

Pingkai output:

```text
- ticket-{alias} | {title} | {created_time} | creator={creator} | assignee={assignee} | l2_assignee={l2_assignee} | l3_assignee={l3_assignee} | module={module}
```

Rules:
- sort by `created_at` descending
- keep exactly one line per ticket
- render empty owner/module fields as `N/A`
- if no tickets match, say `No Pingkai tickets found for the requested filters.`

### list Examples

- `oncall-toolkit list`
- `oncall-toolkit list --status=unresolved --components=tikv --time_range=1w`
- `oncall-toolkit list --status=all --components=tikv,tidb --time_range=7d --assignee="Hongmei Jiang"`
- `oncall-toolkit list --status=all --components=tikv,tidb --time_range=7d --assignee="Hongmei Jiang,Meng Huang"`
- `oncall-toolkit list --source=pingkai --time_range=10d --components=TiKV`
- `oncall-toolkit list --source=pingkai --time_range=10d --assignee="Hongmei Jiang"`
- `oncall-toolkit list --source=pingkai --time_range=10d --assignee="Hongmei Jiang,Meng Huang"`

## close Mode

Use `close` when the user wants to close one or more Jira issues, mainly `TCOC-*` and `GTOC-*`.

`close` supports two execution modes:
- `--preview`:
  - default mode
  - prepare a review package first
  - require a later explicit confirmation before any Jira write
- `--no-preview`:
  - skip the review round
  - execute the close flow directly in the same run

Examples:
- `oncall-toolkit close TCOC-4930`
- `oncall-toolkit close --preview GTOC-8316`
- `oncall-toolkit close --no-preview TCOC-4930`
- `oncall-toolkit close --no-preview TCOC-4930 GTOC-8316`

### close Input Rules

Accepted input:
- optional mode flag: `--preview` or `--no-preview`
- one Jira issue key
- multiple Jira issue keys separated by whitespace or comma

Flag rules:
- if neither flag is present, default to `--preview`
- if `--preview` is present, use the two-step review-first flow
- if `--no-preview` is present, skip the preview package and execute immediately
- if both flags are somehow present, ask the user to choose one instead of guessing

Default scope:
- primarily `TCOC-*` and `GTOC-*`

If the user passes another project key:
- do not close it by default
- ask the user to confirm expansion beyond `TCOC` and `GTOC`

### close Step 1: Review Package (`--preview` only)

In `--preview` mode, the first `close` step is always read-only.
Even if the user asks to close the issue immediately, stop after sending the review package and wait for approval.

In `--no-preview` mode, skip this step entirely.

For each issue:

1. Fetch the issue, status, comments, and available transitions.
2. Generate a compact issue report using the same evidence standards as `report` mode.
3. Inspect whether the issue must first move to `UNDER INVESTIGATION` before it can be resolved.
4. Inspect available transitions that can land in `Resolved`, including a multi-step path when required.
5. Prepare the content that would be used to resolve or close the issue, including draft values for the final resolve fields.
6. Send the package to the user for review.

Do not write any Jira comment.
Do not transition the issue.

### close Review Package Contents

For each issue, provide:

1. `Issue summary`
   - a compact summary block derived from the issue description and comments
2. `Proposed transition`
   - the preferred transition path and final destination status
3. `Fields to fill`
   - every field or content item that should be supplied during close, with proposed values
4. `Draft close comment`
   - the exact comment draft proposed for Jira
5. `Open questions`
   - anything still ambiguous before closing

The review package should be concise but specific enough that the user can edit it directly.

Language rules for the review package:
- `Issue summary` should follow the user's requested language
- `Fields to fill` should use concise English for both labels and proposed values unless the user explicitly asks otherwise
- `Draft close comment` should default to English unless the user explicitly asks for another language
- `Open questions` can follow the user's language

Use this output shape:

```text
### Review {issue_id}
- Issue summary:
- Proposed transition:
- Fields to fill:
- Draft close comment:
- Open questions:
```

`Fields to fill` should include, when applicable:
- resolution or transition-path choice
- close comment body
- any transition-screen fields required by Jira
- any user-provided notes that should be preserved in the closing record

When the final resolve transition exposes these fields, Step 1 should prepare suggested content for each of them and present the values directly for review:
- `Troubleshooting`
- `Rediscovery`
- `Is product issue?`
- `Fix versions`
- `Github Issue(s)`
- `Github Issue`
- `Root Cause (migrated)`
- `Workaround (migrated)`

For these fields:
- do not only list the field names
- draft the proposed value from the issue description, comments, and current evidence
- keep each proposed value concise, usually a short phrase or one short sentence
- use `TBD` when the evidence is insufficient
- if a field appears on the transition screen but the best proposed value is blank, explicitly show it as empty and explain why in `Open questions`

### close Reusable Pattern: No Further Feedback

Use this pattern when:
- the issue has been open for a long time
- no further customer feedback has been received
- the technical conclusion is still incomplete, but the user explicitly wants to close the ticket for now

In this case:
- still prefer `Resolved`, not `Canceled`
- make it clear that the ticket is being closed due to inactivity or lack of follow-up, not due to a fully confirmed root cause
- keep the resolve fields conservative and avoid overstating confidence

Default English `Draft close comment`:

```text
This issue has been open for a long time and we have not received any further feedback. We will close this ticket for now. If there are any further questions or the issue persists, please submit a new ticket.
```

Default `Fields to fill` strategy for this pattern:
- `Troubleshooting`: briefly summarize what was reviewed or requested before follow-up stopped
- `Rediscovery`: briefly describe the original observed symptom
- `Is product issue?`: use `No` unless there is concrete evidence of a product defect
- `Fix versions`: keep empty unless a real fixing version is known
- `Github Issue(s)` or `Github Issue`: keep empty unless there is a confirmed linked GitHub issue
- `Root Cause (migrated)`: say the root cause was not fully confirmed before closure due to lack of further feedback
- `Workaround (migrated)`: ask the customer to submit a new ticket with the missing workload details, logs, or environment information if the issue recurs

### close Step 2: Apply

In `--preview` mode, run this step only after the user explicitly confirms, approves with edits, or says to proceed.

In `--no-preview` mode, run this step immediately in the same command after gathering the required issue data, transitions, and field metadata.

1. Re-fetch the issue and transitions to avoid stale status.
2. Re-fetch transitions with expanded field metadata when available.
3. If the approved path requires an intermediate transition such as `UNDER INVESTIGATION`, execute that transition first.
4. Re-fetch the issue, transitions, and required fields again after any intermediate status change.
5. Apply the user-approved close content for the final resolve step.
6. Add the approved Jira comment if one is part of the close record.
7. Transition the issue to the approved `Resolved` done-state transition.

If the user changes the draft:
- use the edited version, not the original draft

For GitHub-tracking fields:
- prefer the exact Jira field name shown by metadata, not a guessed variant
- if both `Github Issue(s)` and `Github Issue` exist, prefer `Github Issue(s)` as the primary issue-link field
- if the user approved one GitHub issue URL and both fields are writable, it is acceptable to mirror the same URL into both for compatibility

If required transition fields are still missing:
- stop again and ask for the missing values

If the user only partially edits the prepared fields:
- keep the user-edited values as highest priority
- retain the previously reviewed values for the untouched fields

### close Transition Resolution

For each issue:

1. Fetch the issue and its current status.
2. If the issue is already in a done-category status, report it as already closed and skip the transition.
3. If the current status is `PENDING FOR SUPPORT` or a todo-category status such as `To Do`, first prefer a transition to `UNDER INVESTIGATION`.
4. After the issue is in `UNDER INVESTIGATION`, fetch available transitions again.
5. Prefer a transition that lands in a resolved done-category status using this order:
   - `Resolved`
   - `Resolve issue`
6. Use `Close as CANCELED` only when the user explicitly wants to cancel the issue instead of resolving it.
7. If no valid path to `Resolved` is available, do not guess. Report that no suitable resolve path is available.

### close Safety Rules

Before closing:
- verify the key belongs to `TCOC` or `GTOC`, unless the user explicitly asked otherwise
- verify the issue is not already done
- prefer closing only when the issue has an explicit conclusion, workaround, or resolution in the description or comments
- when the issue is in `PENDING FOR SUPPORT` or a todo-category status, move it back to `UNDER INVESTIGATION` before attempting the final resolved transition

If the issue still appears actively under investigation and the user did not explicitly insist on closing it:
- pause and ask for confirmation before transitioning

Comments:
- in Step 1, prepare a draft comment but do not write it
- in Step 2, if the user approved a closing reason or comment, add it before or with the transition
- if the user does not give a reason, do not invent one

### close Output Format

In `--preview` mode, for Step 1 review, use the review package format above.

In `--no-preview` mode, do not emit the review package first unless execution is blocked by missing required information.

For Step 2 execution, for each issue, report a compact result line:

```text
- [{issue_id}](https://tidb.atlassian.net/browse/{issue_id}) | {transition_path}
```

If execution is skipped, report why:
- already closed
- unsupported project
- no resolve path available
- waiting for user confirmation
- missing required fields

## report Mode

Use `report` when the user wants either:
- a compact report for one or more explicit issue ids, or
- a filtered Jira report set by components, time range, and owner.

### report Syntax

Issue-oriented form:

```text
oncall-toolkit report {issue-id...}
```

Filter-oriented form:

```text
oncall-toolkit report [--components=...] [--time_range=...] [--assignee=...]
oncall-toolkit report [--source=...] [--components=...] [--time_range=...] [--assignee=...]
```

### report Routing

First classify whether the input is issue-oriented or filter-oriented.

Issue-oriented report:
- if the arguments contain Jira issue keys, Pingkai `ticket-xxxx` / `TICKET-xxxx` aliases, or an explicit mixed issue list, treat the command as direct issue report mode
- in this mode, support Jira and Pingkai tickets as defined below

Filter-oriented report:
- if the arguments do not look like issue ids, parse them as `report [--source=...] [--components=...] [--time_range=...] [--assignee=...]`
- this form supports Jira-only, Pingkai-only, or aggregate Jira+Pingkai mode depending on `--source`
- use it to find matching tickets, then generate one report block per matched issue or ticket

### report Flags

`--source`:
- accepted: `jira`, `pingkai`, `jira,pingkai`, `pingkai,jira`
- default: `jira`
- `jira,pingkai` and `pingkai,jira` mean aggregate mode across both sources

`--components`:
- accepted examples: `tikv`, `tidb`, `pd`, `ticdc`, `tikv,tidb`
- default: `tikv,tidb,pd`

`--time_range`:
- accepted examples: `1w`, `7d`, `2w`, `30d`, `this_week`
- default: `1w`

`--assignee`:
- accepted for Jira: omitted (current Jira login user), `all`, Jira email, English Jira display name, Jira account id, or a comma-separated list of those values
- do not use Chinese names in Jira JQL; Chinese names remain valid only for Pingkai owner matching
- default: current Jira login user

### report Filter Semantics

Default scope:
- Jira projects: `TCOC`, `GTOC`
- components: `tikv,tidb,pd`
- time range: last `1w`
- target assignee: current Jira login user
- source: `jira`

Components mapping:
- `all` means no Jira component restriction
- otherwise split on commas and match Jira `components` by display name
- if a component token is ambiguous, ask one short clarification question before querying

Time range mapping:
- `1w` or `7d` → `created >= -7d`
- `2w` → `created >= -14d`
- `30d` → `created >= -30d`
- `this_week` → `created >= startOfWeek()`
- if the user gives another clear relative range, convert it to the nearest JQL equivalent

Assignee mapping:
- if `--assignee` is omitted, use the current Jira login user as the default target
- the execution path may resolve the current Jira login user through multiple identifiers such as email, display name, or account ID when needed for reliable matching
- when filtering by `--assignee`, support one or more comma-separated assignees, search both `assignee` and `L2 assignee`, then merge the results
- if the same issue matches both fields, de-duplicate it in the final result
- for Jira, prefer stable identifiers such as email, English display name, or account id; do not emit Chinese names into Jira JQL
- if the user provides a Jira display name or email, resolve it to an account ID first when needed by the execution path
- if `--assignee=all`, do not apply any assignee or `L2 assignee` filter

Pingkai filter scope:
- order by `created_at DESC`
- fetch paginated `/api/v1/tickets`
- continue page-by-page until the oldest ticket in the current page is older than the requested `--time_range`
- no owner filter unless `--assignee` is provided

Aggregate source mode:
- when `--source` contains both `jira` and `pingkai`, query both sources independently
- apply the same `--components`, `--time_range`, and `--assignee` semantics to each source using that source's mapping rules
- merge the matched Jira issues and Pingkai tickets into one result set
- sort the combined output by creation time descending

### report Filter Query Rules

For filter-oriented report, use `jira-cli` as the default Jira path and the Pingkai tickets API as the Pingkai path. Use direct Jira REST API only when CLI output is unavailable or insufficient.

Base filter shape:
- `project in (TCOC, GTOC)`
- optional component clause when `components_list != all`
- one time-range clause
- one owner clause per requested assignee, each expanded as `(assignee = owner OR "L2 assignee" = owner)`
- sort by `created DESC` unless the user asks otherwise

If a single JQL query is not reliable for both owner fields:
- it is acceptable to run two Jira queries and merge client-side before summarizing

For Pingkai filter-oriented report:
- use the tickets list endpoint directly rather than JQL
- filter client-side by `module_name`, `l2_assignee_name`, and `l3_assignee_name`
- keep Pingkai results sorted by `created_at DESC`

For aggregate filter-oriented report:
- query Jira and Pingkai independently
- merge the two result sets client-side
- sort the final combined output by creation time descending

### report Issue-Type Support

For Jira issues, use the Jira-specific resolution rules below.

For Pingkai tickets:
- set `PINGKAI_API_BASE_URL`, `PINGKAI_TICKET_USERNAME`, and `PINGKAI_TICKET_PASSWORD` in the environment before running the scripts
- use `python3 scripts/get_pingkai_ticket.py --alias <ticket_alias> --internal`
- accept only `ticket-xxxx` or `TICKET-xxxx` in `report`
- do not route plain numeric aliases such as `8406` into Pingkai `report`
- do not route 32-char ticket ids into Pingkai `report`
- read the ticket body plus comments in chronological order
- include internal comments by default for analysis and report generation
- prefer `--json` when you need a stable structure for summarization before writing the final answer
- if the ticket contains conflicting comments, prefer later comments over earlier comments
- when a final conclusion appears in a later support or owner comment, use it as the primary source for `Root cause`, `Workaround`, and `TODO`
- derive `Version` from ticket content when explicitly mentioned; otherwise use `N/A`
- if the Pingkai ticket has no reliable workaround or action item recorded, use `None yet` for `Workaround` and leave `TODO` blank

### report Examples

- `oncall-toolkit report TCOC-4930`
- `oncall-toolkit report ticket-8368`
- `oncall-toolkit report TICKET-8368`
- `oncall-toolkit report TCOC-4930 ticket-8368`
- `oncall-toolkit report`
- `oncall-toolkit report --source=pingkai --time_range=1w`
- `oncall-toolkit report --components=tikv --time_range=1w`
- `oncall-toolkit report --source=pingkai,jira --components=tikv --time_range=1w`
- `oncall-toolkit report --components=tikv,tidb --time_range=2w --assignee="Hongmei Jiang"`
- `oncall-toolkit report --components=tikv,tidb --time_range=2w --assignee="Hongmei Jiang,Meng Huang"`

### report Comment Resolution

Find the `L2 assignee` custom field from Jira metadata for the issue project and issue type.

Build the owner author set from:
- `assignee.accountId`
- `L2 assignee.accountId`

In `report`, process Jira fields with these source boundaries:
- `Phenomenon`: summarize from the issue `description` only
- `Root cause`, `Workaround`, `TODO`: summarize from `assignee` and `L2 assignee` comments only

Owner-comment rules:
- read owner comments in chronological order
- when owner comments conflict, prefer the later owner comment
- use owner comments to derive a short conclusion, not a raw line copy
- ignore non-owner comments for `Root cause`, `Workaround`, and `TODO`

Fallback rules:
- if owner comments do not contain an explicit conclusion, do not infer one from reporter/customer comments
- leave `Root cause` blank
- return `None yet` for `Workaround`
- leave `TODO` blank

### report Output Template

`report` is the canonical output mode.

For each issue, output exactly:

```text
{priority_or_source} {title} [{issue_id_or_alias}]({canonical_link})
- Labels:
- Version:
- Phenomenon:
- Root cause:
- Workaround:
- TODO:
```

Derivation rules for Jira:
- `Labels`:
  - render from the real Jira `labels` field
  - join multiple labels with `, `
  - if empty, use `N/A`
- `Version`:
  - prefer Clinic-derived runtime version for `TCOC-*`
  - otherwise use Jira `versions`
  - then `fixVersions`
  - then `N/A`
- `Phenomenon`:
  - use the issue description plus all comments to summarize the observed impact and symptoms
  - if later comments refine the scope or timeframe, use the later wording
- `Root cause`:
  - summarize the latest explicit conclusion after applying the conflict rules above
  - do not use attribution phrasing such as "L2 assignee said"
  - if unknown, leave it blank
- `Workaround`:
  - summarize mitigation or config/query changes from all comments
  - when multiple mitigations are mentioned, prefer the latest owner-confirmed mitigation
  - if unknown, use `None yet`
- `TODO`:
  - extract explicit follow-up actions from all comments
  - when actions conflict, prefer the latest owner conclusion
  - if unknown, leave it blank

Derivation rules for Pingkai:
- `priority_or_source` can use the ticket priority when available; otherwise use `PINGKAI`
- `canonical_link` should point to the ticket URL when available
- `Phenomenon`, `Root cause`, `Workaround`, and `TODO` follow the same plain-English summarization style as Jira summaries
- do not invent Jira-only fields or Jira project semantics

When multiple issues are provided:
- keep the input order
- generate one block per issue
- separate blocks with a blank line

For filter-oriented report results:
- fetch the matching Jira issues first
- then generate one report block per matched issue using the same output template
- sort by created time descending unless the user asks otherwise
- if no issues match, say `No Jira issues found for the requested report filters.`

## RCA Mode

Use `RCA` when the user wants a publication-ready RCA for one Jira issue.

Example:
- `oncall-toolkit RCA TCOC-4930`

### RCA Destinations

Default RCA parent directory:
- `https://pingcap.feishu.cn/wiki/RO1lwXzk6iNMmZk9A4McSML1nec`

Default RCA template:
- `https://pingcap.feishu.cn/wiki/POADwz7w8iLUNtk4NeEcppJWnM0`

Generated title:
- `RCA {issue_id} by AI`

### RCA Input Rules

In RCA mode:
- use the Jira report
- use the Jira description
- use all comments in chronological order
- do not filter to owner comments only
- use later comments to refine or override earlier hypotheses
- keep facts and inferences separate
- use `TBD` when evidence is insufficient

### RCA Structure

Mirror the template structure:
- author / reviewer / last updated table
- `Summary`
- `Timeline`
- `Root Cause Analysis`
- `Solution`
- `TCOC Postmortem`

### RCA Timeline Rules

Build the timeline from the issue description plus all comments in chronological order.

Formatting requirements:
- keep `Timeline` as its own section
- keep each subheading on its own line:
  - `Trigger`
  - `Detection`
  - `Diagnosis`
  - `Mitigation`
  - `Recovery`
  - `Postmortem / Follow-up`
- insert a blank line after each subheading before the first event
- keep each timestamped event on its own line or paragraph
- never concatenate multiple events onto one line
- if plain blank lines collapse in Feishu, use explicit `<br/><br/>` to preserve spacing
- if only the Jira comment timestamp is known, use that timestamp conservatively

### RCA Content Rules

Use the Jira record to draft:
- `Summary`:
  - incident summary
  - Jira link
  - severity
  - cluster scope
  - version
  - user impact
- `Reproduction Steps`:
  - only when it can be reasonably inferred
  - otherwise say no controlled reproduction was recorded
- `Analysis`:
  - summarize the evidence trail from comments and description
- `Conclusion`:
  - state the root cause directly
  - mark it as an inference when it was not stated explicitly
- `Other`:
  - note missing evidence, unresolved questions, or intentionally out-of-scope follow-up incidents
- `Solution`:
  - mitigation
  - workaround
  - long-term fixes

### RCA Feishu Write Rules

When writing the RCA:

1. Fetch the RCA template.
2. Draft markdown that follows the template without repeating the title as the first heading.
3. Create a new child doc under the RCA directory.
4. Title it `RCA {issue_id} by AI`.
5. Do not overwrite an existing doc unless the user explicitly asks to update it.

For the postmortem table:
- preserve the template rows
- fill only what is supported by Jira evidence
- use `TBD` for unknown TCOCP tickets, alerts, or owners

## Guardrails

- Do not invent timestamps, cluster IDs, versions, reviewers, alert IDs, or ticket numbers.
- In `list`, when the requested status is `unresolved`, prefer `statusCategory != Done` over `status != Resolved` for a true open-issue list.
- In `report`, always read all comments in chronological order before concluding.
- In `report`, when comments conflict, apply this priority order: later owner comments > earlier owner comments > later non-owner comments > earlier non-owner comments.
- In `report`, keep the final written output fully in English unless the user explicitly requests another language.
- In `RCA`, always use all comments in chronological order.
- In `close --preview` (and default `close`), Step 1 is always read-only: do not add comments, edit fields, or transition Jira in the review phase.
- In `close --preview` (and default `close`), Step 2 requires explicit user confirmation or user-edited approval before any Jira write.
- In `close --no-preview`, it is acceptable to execute the close flow in one run without waiting for a separate preview round.
- In `close`, never guess the transition ID or assume a status name exists; always inspect available transitions first.
- In `close`, prefer resolving the issue, not canceling it; use `Close as CANCELED` only when the user explicitly asks to cancel.
- In `close`, if the issue is in `PENDING FOR SUPPORT` or a todo-category status, first move it to `UNDER INVESTIGATION` before the final resolve transition.
- In `close`, Step 1 should prepare review-ready draft values for visible resolve fields such as `Troubleshooting`, `Rediscovery`, `Is product issue?`, `Fix versions`, `Github Issue(s)`, `Github Issue`, `Root Cause (migrated)`, and `Workaround (migrated)` whenever they are present on the transition screen.
- In `close`, keep `Fields to fill` concise and in English by default, while `Issue summary` should follow the user's requested language.
- In `close`, re-fetch the issue, transitions, and required transition fields immediately before execution.
- In `close`, after any intermediate transition, re-fetch transitions and required fields again before the final resolve step.
- In `close`, do not invent required transition-screen fields, resolution values, or close-comment content; if Jira requires them and the user has not approved them, stop for review.
- In `close`, when the evidence is insufficient for a resolve-field draft, use `TBD` or explicit blank-with-reason instead of guessing.
- In `close`, when the user wants to close an old inactive ticket due to no further feedback, explicitly say so in the close comment and in `Root Cause (migrated)` rather than implying the technical conclusion is complete.
- In `close`, do not close non-`TCOC` or non-`GTOC` issues unless the user explicitly confirms wider scope.
- If the evidence is incomplete, say so clearly and list the missing evidence.
