Skip to content

Commit e708eed

Browse files
authored
Merge branch 'main' into niharikag09/otel-url-template-resource-name
2 parents 2e8090d + af8c20b commit e708eed

241 files changed

Lines changed: 6868 additions & 1142 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/create-epic-recap/SKILL.md

Lines changed: 248 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# {{recap_title}}: {{epic_key}} — {{epic_summary}}
2+
3+
## Summary
4+
5+
{{summary}}
6+
7+
## Progress
8+
9+
{{progress}}
10+
11+
## What's new for users
12+
13+
{{whats_new}}
14+
15+
## Signal path
16+
17+
{{signal_path}}
18+
19+
## Signal type
20+
21+
{{signal_type}}
22+
23+
## API & config changes
24+
25+
{{api_config_changes}}
26+
27+
## Performance impact
28+
29+
{{performance_impact}}
30+
31+
## Agent footprint
32+
33+
{{agent_footprint}}
34+
35+
## Repositories touched
36+
37+
{{repositories_touched}}
38+
39+
## Customer utilisation tracking
40+
41+
{{customer_tracking}}
42+
43+
## Linked PRs & release notes
44+
45+
{{linked_prs}}
46+
47+
{{pr_discovery_note}}
48+
49+
---
50+
51+
_— Generated by [create-epic-recap](https://github.com/DataDog/datadog-agent/blob/main/.agents/skills/create-epic-recap/SKILL.md)_
52+
53+
<!--
54+
Rendering rules for the skill:
55+
- Replace each `{{placeholder}}` with the rendered value.
56+
- If a section has no data AND is marked optional below, drop the section heading and its body entirely.
57+
- Optional sections: `Signal path`, `Signal type`, `API & config changes`, `Performance impact`, `Agent footprint`, `Customer utilisation tracking`.
58+
- Always-on sections: `Summary`, `What's new for users`, `Repositories touched`, `Linked PRs & release notes`.
59+
- `{{recap_title}}` renders `Resolution recap` (resolution mode) or `Progress update` (progress mode).
60+
- `Progress` section is **progress-mode-only**: render it (with `{{progress}}`) only when `epic_mode == progress`; in `resolution` mode drop the `Progress` heading and `{{progress}}` entirely.
61+
- `{{pr_discovery_note}}` (rendered inside `Linked PRs & release notes`, after `{{linked_prs}}`): the PR-discovery caveat. On a runtime whose MCP server has no dev-status endpoint (e.g. Claude Code/Rovo) it always renders at least the quiet ℹ️ footnote; it expands to the loud ⚠️ warning + MCP-install recommendation whenever a Phase A2 shortfall is detected (`jira_pr_counts[key] > PRs found`). On a runtime with dev-status (Cursor) and no shortfall it renders empty — collapse it without leaving a blank line. See "PR discovery note" in references/pr-discovery.md for the exact text.
62+
- Lists use markdown bullets (`- `). Code/config snippets use fenced code blocks.
63+
- Always keep the attribution footer (separator + "Generated by" line).
64+
- Strip this HTML comment before posting.
65+
-->
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# PR discovery (Step 4 detail)
2+
3+
Find every **merged** PR that resolves the Epic or one of its completed children. Build the list of Jira keys to search for: `[EPIC-KEY, <completed child keys from Step 3>]`.
4+
5+
Two phases run for **all** keys: Phase A reads Jira's Development panel (Cursor only), Phase B searches GitHub (both runtimes). Phase B is the source of truth for PR URLs; Phase A adds high-confidence Tier 0 hits and a cross-validation count.
6+
7+
## Phase A — Jira Development panel (Tier 0)
8+
9+
### A1 — Detail endpoint (full PR info) — Cursor only
10+
11+
> **Claude Code (Rovo): SKIP A1 entirely.** This server has no dev-status / Development-panel tool, so Tier 0 PR URLs cannot be read from Jira. Go straight to A2 (counts) and rely on Phase B for URLs.
12+
13+
For each key, call `jira_get_issue_development_info` on the `user-atlassian` server with:
14+
- `issue_key`: `<KEY>`
15+
- `application_type`: `"GitHub"`**CamelCase is mandatory**; lowercase `"github"` returns empty results (Jira REST API quirk in `/rest/dev-status/1.0/issue/detail`).
16+
- `data_type`: `"pullrequest"`**mandatory**; omitting it causes HTTP 500 with `"message":"dataType"`.
17+
18+
Do **not** use the batch tool `jira_get_issues_development_info` — it returns HTTP 500 when `application_type` is passed.
19+
20+
**Throttling — the dev-status API does not tolerate parallel calls** (parallel requests trigger HTTP 500 rate limiting):
21+
- **Sequential calls only** — one at a time, never in parallel.
22+
- **~1 s pause** between calls (`sleep 1`).
23+
- **Retry once on 500** — wait 5 s, then retry the failing key. If the retry also fails, record the key as "Phase A1 miss" and move on.
24+
- **Fail-fast after 3 consecutive 500s** — stop A1, log `Phase A1 aborted after 3 consecutive 500s, falling back to A2`, and proceed to A2 for **all** keys.
25+
26+
From each successful response, collect `pullRequests` entries where `status == "MERGED"`. Record each as a **Tier 0** PR with: `id` (e.g. `#52248`), `name`, `url`, `status`, `source.branch`, `destination.branch`, `author`, `reviewers`, `lastUpdate`, `repositoryUrl`.
27+
28+
**Tier 0 PRs are auto-included with the highest confidence** — they are explicitly linked to the Jira issue by the GitHub↔Jira integration; no heuristic filtering needed. Deduplicate by `(repository, PR number)` across all keys.
29+
30+
### A2 — Development summary field (PR count validation)
31+
32+
If A1 failed (fail-fast) or returned zero Tier 0 PRs for any key, fetch the cached development summary. This reads a standard Jira custom field via the stable `/rest/api/2/issue/` API (not the flaky `dev-status` endpoint).
33+
34+
Call the **Fetch issue** tool (see `references/runtime-tooling.md`) requesting only `customfield_10000` (the "Development" field):
35+
- **Cursor:** `jira_get_issue``fields: "customfield_10000"`, `comment_limit: 0`. Value under `customfield_10000.value`.
36+
- **Claude Code:** `mcp__atlassian__getJiraIssue``fields: ["customfield_10000"]`. Value is a **string** (see *customfield_10000 shape* below), not an object — extract the embedded `json={…}` first.
37+
38+
In both cases the embedded JSON holds `cachedValue.summary.pullrequest.overall.count` (total linked PRs in **any** state) and `…overall.state` (`MERGED`, `OPEN`, etc.).
39+
40+
**Count merged PRs only — `overall.count` is not comparable.** This skill collects only **merged** PRs, but `overall.count` totals every linked PR regardless of state, producing a false `pr_shortfall` whenever a completed issue still has an open/backport PR linked. Derive a **merged-only** count:
41+
- If the summary exposes a per-state breakdown (`stateCount` for `state: "MERGED"`, or a per-state map), use the merged count directly.
42+
- Otherwise, only treat `overall.count` as the expected merged count when `overall.state == "MERGED"`. If `overall.state` is anything else (`OPEN`, `DECLINED`, mixed), mark this key's merged count **unknown** and skip the shortfall comparison for it.
43+
44+
Record per-key merged counts in a `jira_pr_counts` map: `{ "OTAGENT-307": 4, … }`. Keys with no `pullrequest` in the summary (or null `customfield_10000`) have count 0. Keys whose merged count is unknown are omitted so they never trigger a shortfall.
45+
46+
A2 calls can run **in parallel** (standard Jira API). Batch them with Step 2/3 calls when possible.
47+
48+
**Cross-validation:** after tier classification, for each key with a **known** merged count, compare the number of included PRs (Tier 0 + Tier 1 + Tier 2) against `jira_pr_counts`. Record every key where Phase B found fewer in a `pr_shortfall` list (`{key, jira_count, found_count}`). This drives both the Step 10 preview warning and the report's `{{pr_discovery_note}}` (see *PR discovery note* below).
49+
50+
## Phase B — GitHub search (Tiers 1–4)
51+
52+
**Always run Phase B for all keys** — even those with Tier 0 PRs from A1 — for maximum recall (the Jira integration may miss PRs in other repos or PRs where the key is only in the body).
53+
54+
**One key per request — `OR` is not supported.** `gh search prs` treats the query as a literal string, so `"OTAGENT-410 OR OTAGENT-411"` matches zero PRs. One call per key:
55+
56+
```bash
57+
gh search prs \
58+
--owner DataDog \
59+
--merged \
60+
--limit 20 \
61+
--json repository,title,url,number,labels,author,body \
62+
-- "<KEY>"
63+
```
64+
65+
**Throttling — avoid GitHub's secondary rate limit** (~30 req/min):
66+
- Cap parallelism at **at most 4 concurrent** `gh search prs` calls.
67+
- Sleep **~500 ms between waves** (`sleep 0.5`).
68+
- On HTTP 403 `secondary rate limit`: back off 60 s, then retry the failing keys one at a time.
69+
70+
Notes:
71+
- The `--` separates flags from the query; the bare key goes in the query (no quotes needed inside the JSON shell array).
72+
- `gh search prs` matches the key in title, body, and commit messages — the source of the false positives the tiers filter out.
73+
- Deduplicate by `(repository.nameWithOwner, number)` across all keys **and** against Tier 0 PRs from Phase A. If a PR was already collected as Tier 0, keep it as Tier 0 — do not downgrade.
74+
75+
## Tier classification (Phase B PRs only)
76+
77+
Classify each Phase B PR into one of four tiers, based on where the searched key `<KEY>` appears. This is precision-over-recall by design — Tier 3 is shown in preview so the user can opt-in. Apply tiers in this order; first match wins:
78+
79+
1. **Tier 1 — auto-include** (title match) → include
80+
2. **Tier 4 — cross-reference language in body** → exclude silently
81+
3. **Tier 2 — closing keyword in body** → include
82+
4. **Tier 3 — any other body match** → exclude by default, surface in preview
83+
84+
**Tier 1 — key in PR title.** Word-boundary match `\b<KEY>\b` against `title`. Standard forms: `[OTAGENT-410] …` (bracketed, the standard `team/opentelemetry-agent` format), bare `OTAGENT-410`, `(OTAGENT-410)`.
85+
86+
**Tier 4 — explicit cross-reference language (checked BEFORE Tier 3).** If `\b<KEY>\b` is preceded within 60 chars on the same line by cross-reference language, the PR references but does not resolve the key — drop it silently:
87+
88+
```regex
89+
(?i)\b(follow[- ]?up|related[ -]?to|see also|supersedes|cf\.|referenced in|context for|after|before|companion to|part of)\b[^\n]{0,60}\b<KEY>\b
90+
```
91+
92+
Reasoning: "Follow-up to OTAGENT-392" is not a claim to resolve OTAGENT-392.
93+
94+
**Tier 2 — key with closing keyword in body.** Match the key in `body` only when preceded by a GitHub closing keyword or JIRA-field label, at the start of a logical line (multiline mode):
95+
96+
```regex
97+
^\s*(Resolves|Closes|Fixes|Fix|JIRA|Jira ticket)[:\s]+\b<KEY>\b
98+
```
99+
100+
The `^` anchor (multiline) is critical — it rejects mentions buried inside paragraphs. Treat `\r\n` as line boundaries.
101+
102+
**Tier 3 — key in body, no closing keyword.** Anything else where `\b<KEY>\b` matches in body but Tier 1/2 didn't fire (e.g. `### Motivation\n<URL containing the key>`, bare mentions mid-paragraph, markdown links without a closing keyword). Record as `tier3_candidates` with: PR URL, title, and the 1-2 surrounding body lines containing the key. Surfaced in the Step 10 preview for opt-in.
103+
104+
## Additional drop rules (apply after tier classification, all tiers including Tier 0)
105+
106+
- **Drop reverts** — PRs whose title/body explicitly says "revert" of another PR in the list (keep both if they cancel out — let the user decide during preview).
107+
- **Drop bot PRs**`author.is_bot == true`, or login ending `[bot]`, unless they touch release notes or code. Backport bot PRs (e.g. `[Backport X.Y.x] [OTAGENT-XXX] …`) are deduplicated against the primary PR by Jira key; keep the primary, drop the backport (mention only in a footnote if relevant).
108+
109+
## Zero PRs found
110+
111+
If zero PRs are found across both phases, ask the user via `AskUserQuestion` whether to:
112+
- Provide PR URLs manually (comma-separated)
113+
- Continue with an empty PR section (recap relies on Epic description + user input)
114+
- Cancel
115+
116+
## PR discovery note (`{{pr_discovery_note}}`)
117+
118+
Rendered in Step 9 inside the *Linked PRs & release notes* section, right after `{{linked_prs}}`. It degrades gracefully when the MCP server cannot read Jira's Development panel — pick exactly one variant:
119+
120+
- **Quiet footnote** — when Tier 0 (Phase A1) was unavailable this run (always on Claude Code/Rovo) **and** `pr_shortfall` is empty:
121+
122+
```
123+
> ℹ️ _PR discovery was GitHub-search-only: this MCP server has no dev-status endpoint, so PRs attached only in Jira's Development panel can't be read. The list above may be incomplete._
124+
```
125+
126+
- **Loud warning** — whenever `pr_shortfall` is non-empty (proof that attached PRs were missed). One `- <KEY>: Jira <n>, found <m>` line per entry, then the MCP-install recommendation:
127+
128+
```
129+
> ⚠️ **Some Jira-attached PRs could not be retrieved due to MCP server limitations.**
130+
> Jira's Development panel reports more linked PRs than GitHub search found:
131+
> - OTAGENT-307: Jira 4, found 2
132+
> These PRs are attached in Jira, but their URLs live in the dev-status endpoint, which this MCP server (official Atlassian Rovo) does not expose.
133+
> **To recover them:** connect a community Atlassian MCP server that wraps dev-status — e.g. [`mcp-atlassian`](https://github.com/sooperset/mcp-atlassian) (the `user-atlassian` server in the Cursor setup), which provides `jira_get_issue_development_info`. Then re-run the recap.
134+
```
135+
136+
- **Empty** — when Tier 0 was available (Cursor) **and** `pr_shortfall` is empty. Collapse the placeholder, leaving no blank line.
137+
138+
Keep the tone neutral — it is PM-facing. The MCP-install recommendation appears only in the loud variant, so stakeholders never see developer-setup noise unless PRs were actually missed.
139+
140+
## Claude Code (Rovo) capability gap
141+
142+
The Rovo server exposes **no Development/dev-status endpoint**, so on Claude Code:
143+
144+
- **Phase A1 / Tier 0 is unavailable** — you cannot read linked-PR **URLs** from Jira. Do **not** use remote links as a substitute: `getJiraIssueRemoteIssueLinks` does **not** return GitHub PRs (the integration stores them in dev-status, not remote links — verified empty on OTAGENT-304).
145+
- The only Jira-side PR signal is **Phase A2** (`customfield_10000`): counts + state, never URLs.
146+
- Therefore **all PR URLs come from Phase B (`gh search prs`)**; A2 counts are a weak cross-check. `gh search prs <KEY>` over-matches body/cross-reference mentions, so a count can coincide while the actual PRs differ (e.g. OTAGENT-307: Jira count 4, `gh` also returns 4, but two carry a *different* key in their title). Lean on the Tier 1/2/4 classification and surface Tier 3 in preview rather than trusting a count match.
147+
- **Surface this limitation in the recap itself** via `{{pr_discovery_note}}` (Step 9), not just in the preview.
148+
149+
### customfield_10000 shape (A2)
150+
151+
On Rovo, `customfield_10000` comes back as a **single string**, e.g. `{pullrequest={dataType=pullrequest, state=MERGED, stateCount=4}, json={"cachedValue":{…}}}`. Extract the embedded `json={…}` object and read `cachedValue.summary.pullrequest.overall.count` and `…overall.state`. Keys whose dev field has no `pullrequest` block (or is null) have count 0.
152+
153+
Remember `overall.count` totals **all** linked PRs, not just merged ones. The summary-prefix `state`/`stateCount` pair (`state=MERGED, stateCount=4` above) is the merged-only count when present; otherwise only trust `overall.count` as a merged count when `overall.state == "MERGED"`. When the state is `OPEN`/mixed, treat the merged count as unknown and skip the shortfall comparison for that key.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Runtime & MCP tooling
2+
3+
This skill runs in **two environments** with different Atlassian MCP servers. Detect which one you have at the start of every run, then use the matching column in the tool mapping. Never assume one runtime's tool names exist in the other.
4+
5+
## Detection
6+
7+
- If `jira_get_issue` / `jira_get_issue_development_info` is callable → **Cursor** path (community `mcp-atlassian`, the `user-atlassian` server).
8+
- If `mcp__atlassian__getJiraIssue` is callable → **Claude Code** path (official Atlassian Rovo server).
9+
- If neither, stop and ask the user to connect/authenticate an Atlassian MCP server.
10+
11+
## Tool mapping
12+
13+
| Logical operation | Cursor (`mcp-atlassian`) | Claude Code (Atlassian Rovo) |
14+
|---|---|---|
15+
| Fetch issue | `jira_get_issue``issue_key`, `fields` (CSV), `comment_limit` | `mcp__atlassian__getJiraIssue``cloudId`, `issueIdOrKey`, `fields` (array), `responseContentFormat:"markdown"` |
16+
| Search children | `jira_search``jql`, `fields` (CSV), `limit` | `mcp__atlassian__searchJiraIssuesUsingJql``cloudId`, `jql`, `fields` (array), `maxResults` |
17+
| PR detail / Tier 0 | `jira_get_issue_development_info``issue_key`, `application_type:"GitHub"`, `data_type:"pullrequest"` | **NOT AVAILABLE** — skip Phase A1 entirely (see *Claude Code capability gap* in `references/pr-discovery.md`) |
18+
| Post comment | `jira_add_comment``issue_key`, `body` | `mcp__atlassian__addCommentToJiraIssue``cloudId`, `issueIdOrKey`, `commentBody`, `contentFormat:"markdown"` |
19+
20+
## Key runtime differences
21+
22+
- **Cursor** — no `cloudId` needed; `fields` is CSV; has full Jira Development panel access (Tier 0 PRs).
23+
- **Claude Code**`cloudId` required on every call; `fields` is a JSON array; **no dev-status endpoint** — Phase A1/Tier 0 is unavailable, all PR URLs come from GitHub search (Phase B).
24+
25+
## Reading comments
26+
27+
Comments carry context that is not in the description or PRs, so this skill reads them on the Epic (Step 2) and on relevant child issues (Step 3).
28+
29+
- **Cursor** — pass `comment_limit: <N>` (e.g. `20`) to `jira_get_issue`; the comments come back on the issue payload.
30+
- **Claude Code** — add `"comment"` to the `fields` array of `getJiraIssue` and set `responseContentFormat: "markdown"` so the bodies are readable.
31+
- **Never request the `comment` field in the bulk *Search children* call** — it explodes the response size (see *Large responses*). Fetch comments per issue with individual *Fetch issue* calls instead, and cap how many issues / comments you pull.
32+
33+
## JQL for Epic children
34+
35+
Use `parent = <EPIC-KEY>` — works on both runtimes and modern Jira hierarchies. Fall back to `"Epic Link" = <EPIC-KEY>` (double quotes exactly as shown) only if `parent =` is rejected on a classic project.
36+
37+
## cloudId (Claude Code only)
38+
39+
Pass `cloudId: "datadoghq.atlassian.net"` directly — the site hostname works. If a call rejects it as invalid/unknown, call `mcp__atlassian__getAccessibleAtlassianResources` once and use the returned `id` (UUID) for the `datadoghq` site.
40+
41+
## Large responses (Claude Code)
42+
43+
`searchJiraIssuesUsingJql` can exceed the MCP response token cap and be spilled to a file. To avoid/handle this, request **only the fields you need** (e.g. `["summary","status","issuetype","customfield_10000"]`); if the result is saved to a file, parse it with `jq` instead of re-reading the whole blob.

.agents/skills/create-pr/SKILL.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Create a pull request for the current branch following the Datadog Agent contrib
3939
- **Motivation**: A reason why the change is made. Point to an issue if applicable. Include drawbacks or tradeoffs if any.
4040
- **Describe how you validated your changes**: How you validated the change (tests added/run, benchmarks, manual testing). Only needed when testing included work not covered by test suites.
4141
- **Additional Notes**: Any extra context, links to predecessor PRs if part of a chain, notes that make code understanding easier. **Only include this section if there is genuinely useful context to add** — omit it entirely rather than filling it with filler.
42+
11. Once the PR is pushed, ask the user if they want to follow CI status for this PR. If yes, invoke the `/follow-pr` skill.
4243

4344
## PR Description Guidelines (from CONTRIBUTING.md)
4445

@@ -86,4 +87,5 @@ EOF
8687

8788
## Output
8889

89-
Return the PR URL when done.
90+
If you are not following the PR status (step 11): Return the PR URL when done.
91+
Otherwise, defer to `/follow-pr`.

0 commit comments

Comments
 (0)