Skip to content

Commit 7582cb3

Browse files
Merge pull request #3 from RiveryIO/feat/jira-feature-velocity-tracker
feat: add Jira feature velocity tracker with LLM classification
2 parents 402231d + a2e75ca commit 7582cb3

7 files changed

Lines changed: 496 additions & 3 deletions

File tree

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
---
2+
name: jira-features
3+
description: Pulls resolved Jira tickets via Atlassian MCP, groups them into features by Epic, uses LLM classification to determine user-facing vs internal, and writes features-released.csv. Use when the user asks to fetch Jira features, update the feature velocity CSV, track released features, measure feature velocity, or sync Jira data.
4+
---
5+
6+
# Jira Feature Velocity Tracker
7+
8+
Fetches Done tickets from Jira boards (one per team), groups them into features by Epic, classifies each as user-facing via LLM reasoning, and writes `features-released.csv`.
9+
10+
## Prerequisites
11+
12+
- Atlassian MCP server connected (`user-mcp-atlassian`)
13+
- `jira-teams.yaml` in project root (template ships with the repo; board IDs populated via Step 1 below)
14+
15+
## Workflow
16+
17+
Follow **all five steps** in order. Step 1 is one-time setup; Steps 2-5 run each time.
18+
19+
---
20+
21+
### Step 1 -- Board Discovery (one-time)
22+
23+
Populate `jira-teams.yaml` with real board IDs.
24+
25+
1. Call `jira_search_fields` via MCP with keyword `"story point"` to find the custom field ID for story points. Save it in `jira-teams.yaml` under `settings.story_points_field`.
26+
27+
2. Call `jira_get_agile_boards` via MCP (limit 50, paginate if needed) to list all boards.
28+
29+
3. For each team in `jira-teams.yaml` (Core, FullStack, Integration, CDC, Ninja, Devops), find the board whose name best matches the team name. Present the mapping to the user for confirmation:
30+
31+
```
32+
Proposed board mapping:
33+
Core -> board 42 "Core Team Board"
34+
FullStack -> board 43 "FullStack Board"
35+
...
36+
```
37+
38+
4. After confirmation, update `jira-teams.yaml` with the real `board_id` and `board_name` values.
39+
40+
---
41+
42+
### Step 2 -- Fetch Done Tickets
43+
44+
For each team/board in `jira-teams.yaml`:
45+
46+
1. Build the JQL query:
47+
48+
```
49+
project = <board_project> AND status in ("Done", "Closed", "Released") AND resolved >= "-{days}d"
50+
```
51+
52+
Default `{days}` is from `settings.default_lookback_days` (90). Override with user-supplied value.
53+
54+
2. Call `jira_search` via MCP with:
55+
- `jql`: the query above
56+
- `fields`: `summary,description,issuetype,status,resolutiondate,created,parent,labels,components,fixVersions,{story_points_field}`
57+
- `limit`: 50
58+
- `start_at`: 0 (paginate until all results fetched)
59+
60+
3. Collect all tickets across all teams. Tag each ticket with its `team` name from the board mapping.
61+
62+
4. Optionally save raw results to `cache/jira-raw-{date}.json` for debugging.
63+
64+
---
65+
66+
### Step 3 -- Epic Grouping & Dedup
67+
68+
Group the fetched tickets into "features":
69+
70+
1. **Epic-parented tickets**: Group all tickets that share the same `parent` (Epic key) into one feature.
71+
- `feature_id` = the Epic key (e.g., `PROJ-100`)
72+
- `feature_name` = the Epic's summary (fetch via `jira_get_issue` if not already available)
73+
- `jira_keys` = list of all child ticket keys in this group
74+
- `released_date` = the latest `resolutiondate` among the group
75+
- `first_created` = the earliest `created` date among the group
76+
- `story_points` = sum of story points across all tickets in the group
77+
- `fix_versions` = union of all fix versions
78+
79+
2. **Orphan tickets** (no Epic parent): Each is its own feature.
80+
- `feature_id` = the ticket key
81+
- `feature_name` = the ticket summary
82+
- `jira_keys` = just this one key
83+
84+
3. **Bug/Defect handling**: If `issuetype` is `Bug` or `Defect`, set `category = bug_fix` regardless of Epic grouping. Bugs under an Epic are still grouped with that Epic but the category reflects the bug nature. If an Epic contains ONLY bugs/defects, the whole feature is `category = bug_fix`. Mixed Epics (bugs + stories) keep `category = feature`.
85+
86+
---
87+
88+
### Step 4 -- LLM Classification
89+
90+
For each feature group, classify using your own reasoning (you ARE the LLM):
91+
92+
1. **`is_user_facing`** (true/false): Does this feature directly impact what end users see or experience? Consider:
93+
- Changes to UI, API responses, user flows, notifications = user-facing
94+
- Internal tooling, CI/CD, refactors, monitoring, infra = NOT user-facing
95+
- Bug fixes that affect user experience = user-facing
96+
- Performance improvements users would notice = user-facing
97+
98+
2. **`category`**: One of:
99+
- `feature` -- new capability or significant enhancement
100+
- `bug_fix` -- fixes broken behavior
101+
- `improvement` -- incremental enhancement to existing capability
102+
- `tech_debt` -- refactoring, cleanup, dependency updates, infra
103+
104+
3. **`description`**: One-line summary of what was shipped, written for a non-technical audience.
105+
106+
4. **`llm_reasoning`**: One sentence explaining the classification decision (audit trail).
107+
108+
Present the classification results to the user in a summary table before writing. Example:
109+
110+
```
111+
| feature_id | feature_name | team | category | user_facing | released |
112+
|------------|------------------------|-----------|----------|-------------|------------|
113+
| PROJ-100 | New onboarding flow | FullStack | feature | yes | 2026-03-01 |
114+
| PROJ-205 | Fix login timeout | Core | bug_fix | yes | 2026-02-28 |
115+
| PROJ-310 | Upgrade Redis driver | Devops | tech_debt| no | 2026-02-25 |
116+
```
117+
118+
Ask user to confirm or adjust any classifications before proceeding.
119+
120+
---
121+
122+
### Step 5 -- Write CSV
123+
124+
1. Build the JSON array of classified features matching the schema expected by `scripts/jira_features_to_csv.py`.
125+
126+
2. Write the JSON to a temp file or pipe to stdin:
127+
128+
```bash
129+
python scripts/jira_features_to_csv.py --input features.json
130+
```
131+
132+
Or for a preview first:
133+
134+
```bash
135+
python scripts/jira_features_to_csv.py --input features.json --dry-run
136+
```
137+
138+
3. Report the result: how many features were written (new vs updated).
139+
140+
---
141+
142+
## CSV Schema: `features-released.csv`
143+
144+
| Column | Type | Description |
145+
|--------|------|-------------|
146+
| `feature_id` | string | Epic key or standalone issue key (primary key) |
147+
| `feature_name` | string | Epic summary or LLM-generated name |
148+
| `jira_keys` | string | Pipe-separated constituent ticket keys |
149+
| `ticket_count` | int | Number of tickets in this feature |
150+
| `category` | enum | `feature` / `bug_fix` / `improvement` / `tech_debt` |
151+
| `is_user_facing` | bool | LLM-classified: impacts end users? |
152+
| `llm_reasoning` | string | Classification rationale |
153+
| `team` | string | Team name from jira-teams.yaml |
154+
| `released_date` | date | Latest resolution date in the group |
155+
| `first_created` | date | Earliest creation date in the group |
156+
| `lead_time_days` | int | `released_date - first_created` (computed) |
157+
| `quarter` | string | e.g., `2026-Q1` (computed) |
158+
| `iso_week` | string | e.g., `2026-W10` (computed) |
159+
| `story_points` | float | Sum of story points |
160+
| `description` | string | LLM-generated one-liner |
161+
| `fix_versions` | string | Jira fix versions (pipe-separated) |
162+
163+
## Velocity Queries This Enables
164+
165+
- **Features shipped per week by team**: group `iso_week` + `team` where `is_user_facing = true`
166+
- **Throughput by quarter**: count by `quarter` + `team`
167+
- **Feature vs bug ratio**: `category` breakdown per team
168+
- **Average lead time**: mean `lead_time_days` per `quarter` + `team`
169+
- **Story points velocity**: sum `story_points` per `iso_week` + `team`
170+
171+
## Options
172+
173+
| Option | Default | Description |
174+
|--------|---------|-------------|
175+
| `--days` | 90 | Lookback window for resolved tickets |
176+
| `--team` | all | Restrict to a single team |
177+
| `--dry-run` | false | Preview CSV changes without writing |
178+
179+
## Notes
180+
181+
- The Bots team from `teams.cfg` is excluded (no Jira board for bots).
182+
- Derived columns (`lead_time_days`, `quarter`, `iso_week`) are computed by `jira_features_to_csv.py`, not by the agent.
183+
- Re-running is safe: features are upserted by `feature_id`, so re-classification updates existing rows.

.github/workflows/pr-complexity.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ on:
77
jobs:
88
analyze:
99
runs-on: ubuntu-latest
10+
if: ${{ secrets.OPENAI_API_KEY != '' }}
1011
permissions:
1112
pull-requests: write
1213
contents: read

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ Thumbs.db
5151
# Results/data files
5252
*.csv
5353
!complexity-report.csv
54+
!features-released.csv
5455
*.png
5556
results/
5657

0 commit comments

Comments
 (0)