# Pm Risks

> Risk radar for a Huly tracker project — surfaces overdue issues, at-risk milestones, unassigned high-priority work, stuck in-progress, bottlenecks, and over-budget items. Output only, no writes to Huly. Trigger on: /pm-risks, 'project risks', 'what's at risk', 'risk report for PROJ', 'PM risks', 'risk radar'.

- Skill: `yotharit/pm-risks` (Agent Skill)
- Install (CLI): `npx skillmds@latest add yotharit/pm-risks`
- Raw SKILL.md: https://api.skillmd.com/api/skills/yotharit/pm-risks/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: yotharit (https://skillmd.com/u/yotharit)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/yotharit/pm-risks

---


# /pm-risks

Risk radar for a Huly tracker project. Surfaces issues / milestones that need PM attention this week. **Output only — never writes to Huly.**

**Requires** the `huly` MCP server.

## Args

```
/pm-risks <PROJECT> [--stale-days 7] [--block-threshold 3]
```

- `PROJECT` — identifier or `_id`. **Required.** If missing, ask.
- `--stale-days` — threshold for "stuck" rule. Default `7`.
- `--block-threshold` — minimum dependents for the "bottleneck" rule. Default `3`.

## Steps

1. `huly_list_statuses` `{ project }` — map status `_id` → name + category, identify which statuses are `done` / `cancelled`.
2. `huly_list_issues` `{ project, limit: 500 }` — full dataset.
3. `huly_list_milestones` `{ project }` — milestone status + target dates.
4. `huly_get_detailed_time_report` `{ project }` — used for over-budget rule.
5. For top in-progress / high-priority issues, call `huly_list_issue_relations` `{ identifier }` to count how many issues each one blocks. (Only fan out for the top ~30 candidates to keep latency reasonable.)

## Risk rules

Apply each rule against the dataset. Each rule produces zero or more risk records with `{severity, rule, identifier|milestone, title, details}`.

| Severity | Rule | Condition |
|---|---|---|
| 🔴 HIGH | Overdue | `dueDate < now` AND status not in done/cancelled |
| 🔴 HIGH | Milestone at risk | Milestone `status == 1 (in-progress)` AND `target_date < now + 7d` AND `> 30%` of its issues still open |
| 🔴 HIGH | Critical bottleneck | Issue blocks ≥ `block-threshold * 2` other issues AND `modifiedOn > stale-days days ago` |
| 🟡 MED  | Unassigned high-priority | `priority in (1=urgent, 2=high)` AND `assignee == null` |
| 🟡 MED  | Stuck | status in-progress (category `Started`) AND `modifiedOn > stale-days days ago` |
| 🟡 MED  | Bottleneck | Issue blocks ≥ `block-threshold` others (and not already in critical) |
| 🟢 WATCH | Over-budget | `estimation > 0` AND total logged time / estimation > 1.5 |
| 🟢 WATCH | Sprint slipping | Active milestone where required pace > 2× actual pace this week |

(Pace = issues per remaining day to hit 100% open-closed by target_date; actual pace = issues closed in the last 7 days / 7.)

## Severity ordering

Within each severity tier, sort by:
1. Number of dependents (bottlenecks first).
2. Days overdue / days stuck (descending).
3. Priority (urgent → low).

## Output format

```
Risks — [PROJECT] · [YYYY-MM-DD]

🔴 HIGH ([count])
  1. [PROJ-N] [title] · overdue [n]d · @[assignee]
  2. [Sprint name] at risk · [open]/[total] open · target in [n]d
  3. [PROJ-N] [title] · blocks [n] issues · stuck [n]d
  ...

🟡 MEDIUM ([count])
  [k]. [PROJ-N] [title] · urgent, unassigned
  [k]. [PROJ-N] [title] · in-progress [n]d, no update
  ...

🟢 WATCH ([count])
  [k]. [PROJ-N] [title] · est [n]h, logged [n]h ([×ratio])
  ...

Recommend:
  • [1 line, derived from the top HIGH item]
  • [1 line, derived from sprint risk if present]
  • [1 line, derived from largest bottleneck]
```

If there are zero risks in a tier, omit the tier header entirely. If zero risks total, say:

```
Risks — [PROJECT] · [YYYY-MM-DD]

No active risks detected.
Scope: [n] open · [n] in-progress · sprint on pace.
```

## Computation notes

- **Relations check (bottleneck):** the `is-blocked-by` direction comes from looking at OTHER issues' relations pointing at this one. Practical approach: for each issue, the `blocks` count = number of issues whose `relations` array contains an entry with `_class == 'tracker:relation:Blocks'` AND `relatedTo == thisIssue._id`. Compute this in one pass over the full issue set rather than calling `huly_list_issue_relations` 500 times.
- **Days overdue / stuck:** integer days, floor of `(now - timestamp) / 86_400_000`.
- **Sprint pace:**
  ```
  open = issues in milestone with status not done/cancelled
  total = issues in milestone
  required_pace = open / max(1, days_until(target_date))
  actual_pace = issues_closed_in_last_7d_in_milestone / 7
  ```

## Edge cases

- **Project with no milestones** — skip the "milestone at risk" and "sprint slipping" rules cleanly. Don't fabricate.
- **Issues with `dueDate == null`** — they can never trigger the overdue rule. That's correct.
- **`estimation == 0` or null** — over-budget rule does not apply. Skip.
- **Very large project (>500 issues)** — note in the footer that the analysis covers the first 500 issues by `modifiedOn`; suggest using a tighter project scope or a milestone filter.

## Don't

- Don't call any `huly_create_*`, `huly_update_*`, or `huly_delete_*` tool.
- Don't write to a teamspace, even if asked — direct the user to copy the output.
- Don't include risks for archived projects without explicit user confirmation.
- Don't speculate: if a rule fires on a flaky signal (e.g. modifiedOn just-now but assignee changed), mention the data source so the PM can verify.

