Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ Tactical, Jira-focused agents for day-to-day backlog operations.
> Skills: `process-interview`

**@backlog-curator** — Backlog Health
> Scans for stale tickets, missing fields, duplicates, and wrong statuses. Prepares grooming agendas. Suggests priority rebalancing when priorities drift.
> Scans for stale tickets, missing fields, duplicates, and wrong statuses. Prepares grooming agendas. Records each grooming session to an auditable report. Suggests priority rebalancing when priorities drift.
>
> Skills: `backlog-health-check` · `grooming-prep` · `priority-rebalance`
> Skills: `backlog-health-check` · `grooming-prep` · `grooming-report` · `priority-rebalance`

**@intake-triager** — Incoming Work
> Classifies new tickets (bug vs task vs epic), searches for duplicates, assesses severity and effort, drafts acceptance criteria. Recommends priority and parent epic.
Expand Down Expand Up @@ -124,6 +124,7 @@ Strategic, outward-facing agents for roadmap, market analysis, and stakeholder c
| **suggest-improvement** | Files issues/PRs against this repo when agents find gaps |
| **backlog-health-check** | Scans for stale, incomplete, orphaned, or duplicate tickets |
| **grooming-prep** | Prepares grooming session agendas with readiness assessments |
| **grooming-report** | Logs grooming actions to an audit trail and renders a per-session Markdown report |
| **priority-rebalance** | Reviews priority distribution and suggests adjustments |
| **intake-triage** | Classifies tickets, finds duplicates, recommends fields |
| **impact-assessment** | Evaluates severity, urgency, and effort with Jira context |
Expand Down Expand Up @@ -159,7 +160,7 @@ This creates a feedback loop: real-world usage continuously improves the agents.
module/
├── AGENTS.md # Module overview for AI assistants
├── mcps.json # MCP server configuration
├── skills/ # 23 skills (each in <name>/SKILL.md)
├── skills/ # 24 skills (each in <name>/SKILL.md)
├── agents/ # 8 agent definitions
└── commands/ # Slash commands (jira-setup, process-interview)
marketplace.yml # Lola marketplace definition (optional)
Expand Down
3 changes: 2 additions & 1 deletion module/agents/backlog-curator.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name: backlog-curator
description: Keeps the Jira backlog healthy, prioritized, and grooming-ready
tools: [mcp__mcp-atlassian__jira_search, mcp__mcp-atlassian__jira_get_issue, mcp__mcp-atlassian__jira_get_transitions, mcp__mcp-atlassian__jira_create_issue, mcp__mcp-atlassian__jira_update_issue, mcp__mcp-atlassian__jira_transition_issue, mcp__mcp-atlassian__jira_add_comment]
skills: [backlog-health-check, grooming-prep, priority-rebalance, suggest-improvement]
skills: [backlog-health-check, grooming-prep, grooming-report, priority-rebalance, suggest-improvement]
---

## Purpose
Expand All @@ -19,6 +19,7 @@ Maintains backlog quality so the Product Owner can focus on decisions rather tha
- When checking field completeness, use the required fields from the workflow config, not assumptions.
- Flag duplicates as candidates for review, don't close them unilaterally.
- When preparing grooming, focus on making tickets actionable: are descriptions clear? Are acceptance criteria present? Are dependencies identified?
- When you make changes during a grooming session, record them with the `grooming-report` skill: log each action to the audit trail and regenerate the Markdown report so the session is auditable and reviewable by the team.

## Self-Improvement

Expand Down
95 changes: 95 additions & 0 deletions module/skills/grooming-report/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
name: grooming-report
description: Record every backlog grooming action to an append-only audit log and render a human-readable Markdown report of each grooming session
---

## When to Use

During and after a backlog grooming session. Use this alongside `grooming-prep`
and `backlog-health-check`: those decide *what* to do, this one keeps a durable
record of *what was done* so sessions are auditable and reviewable.

## Why

Grooming touches many tickets across a session. Without a record, there is no way
to answer "what changed last Tuesday?", to review automated actions after the fact,
or to hand a summary to the team. This skill maintains a machine-readable audit
trail and turns it into a report the PO and team can read.

## Where the Files Live

Keep the audit trail in the gitignored `local/` directory so it stays with the
user's workflow config and out of version control:

- `local/grooming_log.jsonl` — append-only audit trail (one JSON object per line)
- `local/grooming_report.md` — generated report (do not edit by hand)

## Instructions

Read `local/jira-workflow.md` first to understand the project's conventions
(issue types, hierarchy, required fields, status names).

### 1. Mark the start of the session

Before making any changes, append a session marker to
`local/grooming_log.jsonl`:

```jsonl
{"session_start": "YYYY-MM-DD", "groomer": "Name", "total_issues": N, "analyzed": M, "note": "what this session focused on"}
```

- `total_issues` — how many issues matched the grooming query
- `analyzed` — how many you actually reviewed this session

### 2. Log every action as you go

Append one line per action. Use a small, consistent set of `action` values so
the report stays scannable. Common actions:

```jsonl
{"issue": "KEY-123", "action": "updated", "changes": ["added_label_X", "assigned_to_parent_KEY-100"], "notes": "context", "timestamp": "ISO8601"}
{"issue": "KEY-456", "action": "postponed_for_team_review", "reason": "needs_team_input_on_priority", "timestamp": "ISO8601"}
{"action": "created_epic", "issue": "KEY-789", "summary": "Epic title", "parent": "PARENT-KEY", "timestamp": "ISO8601"}
{"action": "created_feature", "issue": "KEY-790", "summary": "Feature title", "parent": "PARENT-KEY", "timestamp": "ISO8601"}
```

Field conventions the report understands:

- `issue` / `issues` — a single key or an array of keys the action applies to
- `action` — the verb (`updated`, `postponed_for_team_review`, `created_epic`,
`created_feature`, or any project-specific verb)
- `changes` — array (or string) of concrete changes made
- `notes` / `reason` — free-text context; the report falls back to whichever is present
- `summary` / `parent` — used for `created_*` actions
- `timestamp` — ISO 8601, when the action happened

Only log actions that actually succeeded. If a change failed, note it in `notes`.

### 3. Close out the session (optional)

A summary line is convenient for at-a-glance totals:

```jsonl
{"summary": "grooming_complete", "total_processed": N, "epics_created": N, "postponed": N, "timestamp": "ISO8601"}
```

Lines without an `action` and without `issue`/`issues`/`features` are skipped by
the report generator, so summary-only lines are safe to include.

### 4. Generate the report

Run the bundled script to render the log as Markdown:

```bash
./generate-md-report.sh local/grooming_log.jsonl local/grooming_report.md
```

It groups entries by session and produces a table of actions per session. The
script requires `jq`. Both arguments are optional and default to
`grooming_log.jsonl` / `grooming_report.md` in the current directory.

### 5. Report back to the PO

Point the PO at `local/grooming_report.md` and give a one-paragraph summary of the
session: how many issues were analyzed, what was changed automatically, and what
was postponed for team review.
97 changes: 97 additions & 0 deletions module/skills/grooming-report/generate-md-report.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/bin/bash
# Convert a grooming audit log (JSONL) to a human-readable Markdown report.
#
# Usage: ./generate-md-report.sh [input.jsonl] [output.md]
# Defaults: grooming_log.jsonl -> grooming_report.md
#
# Requires: jq

JSONL_FILE="${1:-grooming_log.jsonl}"
OUTPUT="${2:-grooming_report.md}"

if ! command -v jq >/dev/null 2>&1; then
echo "Error: jq is required but not installed"
exit 1
fi

if [[ ! -f "$JSONL_FILE" ]]; then
echo "Error: $JSONL_FILE not found"
exit 1
fi

# Start markdown
cat > "$OUTPUT" << 'EOF'
# Backlog Grooming History

This report is auto-generated from the grooming audit log.

**To regenerate:** `./generate-md-report.sh <input.jsonl> <output.md>`

---

EOF

# Group by session
while IFS= read -r line; do
# Check if it's a session_start marker
session=$(echo "$line" | jq -r 'select(.session_start != null) | .session_start' 2>/dev/null)

if [[ -n "$session" ]]; then
# New session
total=$(echo "$line" | jq -r '.total_issues // "-"')
analyzed=$(echo "$line" | jq -r '.analyzed // "-"')
groomer=$(echo "$line" | jq -r '.groomer // "-"')

{
echo ""
echo "## Session: $session"
echo ""
echo "**Groomer:** $groomer "
echo "**Total issues:** $total "
echo "**Analyzed:** $analyzed"
echo ""
echo "| Issue | Action | Changes | Notes |"
echo "|-------|--------|---------|-------|"
} >> "$OUTPUT"
else
# Regular entry
action=$(echo "$line" | jq -r '.action // ""')

# Skip entries without action field (summary-only entries)
if [[ -z "$action" ]]; then
continue
fi

# Handle issue vs issues vs features (single vs array)
issue=$(echo "$line" | jq -r 'if .issues then (.issues | if type == "array" then join(", ") else . end) elif .issue then .issue elif .features then (.features | if type == "array" then join(", ") else . end) else "" end')

# Skip entries without issue/issues/features field
if [[ -z "$issue" ]]; then
continue
fi

# Handle changes array or string
changes=$(echo "$line" | jq -r 'if .changes then (.changes | if type == "array" then join(", ") else . end) else "-" end')

# Combine notes and reason
notes=$(echo "$line" | jq -r '.notes // .reason // .summary // "-"')

# If it's a created_feature or created_epic, format differently
if [[ "$action" == "created_feature" ]] || [[ "$action" == "created_epic" ]]; then
summary=$(echo "$line" | jq -r '.summary // "-"')
parent=$(echo "$line" | jq -r '.parent // "-"')
echo "| **$issue** | $action | Parent: $parent | $summary |" >> "$OUTPUT"
else
echo "| $issue | $action | $changes | $notes |" >> "$OUTPUT"
fi
fi
done < "$JSONL_FILE"

{
echo ""
echo "---"
echo ""
echo "*Generated: $(date -u +"%Y-%m-%d %H:%M:%S UTC")*"
} >> "$OUTPUT"

echo "Generated $OUTPUT from $JSONL_FILE"