Skip to content
Back to skills

Review Epic

ASecurity

Use when asked to review epic health, audit stalled children, check scope drift, find missing coverage, or assess closure readiness. Produces a structured health report and actionable recommendations. Read-only.

  • 6 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 6, 2026
ai-agentsgobashrailsgit

Works with

  • terminal
  • cli

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned October 6, 2026

npx -y skills add BrennonTWilliams/little-loops --skill review-epic --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Review Epic?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Review Epic
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/brennontwilliams-review-epic-little-loops/badge)](https://www.skillsdirectory.com/skills/brennontwilliams-review-epic-little-loops)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: review-epic
description: Use when asked to review epic health, audit stalled children, check scope drift, find missing coverage, or assess closure readiness. Produces a structured health report and actionable recommendations. Read-only.
argument-hint: "<EPIC-ID> [--skip-drift]"
allowed-tools:
  - Read
  - Bash(ll-issues:*)
  - Bash(git:*)
  - Bash(ll-history-context:*)

arguments:
  - name: epic_id
    description: "EPIC ID to review (e.g., EPIC-42)"
    required: true
  - name: flags
    description: "--skip-drift to skip scope-drift and missing-coverage LLM passes (structural-only, fast mode)"
    required: false
metadata:
  short-description: "Audit EPIC health: stalled children, scope drift, missing coverage."
trigger_fixtures:
  should_fire:
    - "review epic health and audit stalled children for scope drift"
    - "assess this epic's closure readiness and find missing coverage"
  should_not_fire:
    - "decompose this theme into an epic with child issues"
    - "detect conflicting requirements across open issues"
---

# Review Epic

Audits an EPIC's children for stalled status, scope drift, missing coverage, and closure
readiness. Outputs a structured health report and actionable recommendations. Never
writes to any file — all suggestions require the user to invoke a follow-up command.

---

## Step 1: Parse Arguments

Extract from `$ARGUMENTS`:

- Set `EPIC_ID` to the first token (e.g., `EPIC-42`). Uppercase it.
- Set `SKIP_DRIFT=true` if `--skip-drift` is present in flags.
- Read `STALE_DAYS` from `{{config.commands.review_epic.stale_days}}` (default 14).
- Read `ENABLE_SCOPE_DRIFT` from `{{config.commands.review_epic.enable_scope_drift_check}}`
  (default true). Force-disable if `SKIP_DRIFT=true`.

---

## Step 2: Load EPIC and Resolve Children

### 2a. Verify EPIC exists

```bash
ll-issues list --status all --type EPIC --json
```

Parse the JSON output. Find the record where `issue_id == EPIC_ID`.

**If not found**, report and stop:

```
✗ EPIC not found: EPIC_ID
  Run `ll-issues list --type EPIC` to see available EPICs.
```

Record the EPIC's `path`, `title`, `status`, and `relates_to` list.

### 2b. Load all issues for child resolution

```bash
ll-issues list --status all --json
```

Parse the JSON list as `all_issues`.

### 2c. Resolve children

Use `parent:` backrefs only — mirrors `compute_epic_progress()` in
`little_loops.issue_progress`):

```
relates_to_ids       = set of IDs from the EPIC's `relates_to` list (recorded in Step 2a)
child_ids            = {issue_id for issue in all_issues if issue.parent == EPIC_ID}
children             = [issue for issue in all_issues if issue.issue_id in child_ids]
related_not_children = [issue for issue in all_issues
                        if issue.issue_id in relates_to_ids
                        and issue.issue_id not in child_ids]
```

**If no children**, report and stop:

```
================================================================================
EPIC HEALTH REPORT: EPIC_ID — EPIC title
================================================================================

This EPIC has no linked children.

Next steps:
  • /ll:capture-issue -- with "parent: EPIC_ID" to create a child issue
  • /ll:link-epics to assign orphaned issues to this EPIC

================================================================================
```

---

## Step 3: Compute Progress Aggregates

```bash
ll-issues epic-progress EPIC_ID --format json
```

Parse the JSON output for `by_status`, `percent_done`, `total`, and `oldest_open`.

Compute:
- `done_count`    = sum of `by_status` values for `done` and `cancelled`
- `active_count`  = sum for `open`, `in_progress`, `blocked`
- `deferred_count` = `by_status.deferred` or 0
- `oldest_open_age` = `oldest_open.age_days` from the JSON (or null if none)

---

## Step 4: Stall Detection (Non-LLM)

For each child issue, fetch effort context to surface in the health report:

```bash
EFFORT=$(ll-history-context --for-skill review-epic --effort CHILD_ISSUE_ID 2>/dev/null || true)
```

If `$EFFORT` is non-empty, include session count and cycle time in the health report
alongside the child issue's status.

For each child with status `open`, `in_progress`, or `blocked`:

1. Read the child issue file content.
2. Extract the last-activity date using the session-log pattern (same logic as
   `_parse_updated_date()` in `little_loops.cli.issues.search`):
   - Scan the `## Session Log` section for the most recent timestamp line matching
     `` - `/ll:cmd` - YYYY-MM-DDTHH:MM:SS - `path` ``
   - Parse the date portion `YYYY-MM-DD` as the last-activity date.
   - If no session-log entries, fall back to the file's modification date:
     ```bash
     date -r "PATH_TO_FILE" +"%Y-%m-%d"
     ```
3. Compute `days_since_activity = today - last_activity_date`.
4. Flag as **stalled** if `days_since_activity > STALE_DAYS`.

Collect all stalled children as `stalled_children` list with their
`{id, title, status, days_since_activity}`.

---

## Step 5: Scope Drift Classification (LLM Pass)

**Skip this step if `ENABLE_SCOPE_DRIFT=false`.**

Read the EPIC file's `## Summary` section (text after `## Summary` heading, before the
next `##` heading).

For each active child (`open`, `in_progress`, `blocked`), read its `## Summary` section.

Classify each child using the following scoring table. Read the EPIC summary and each
child summary carefully, then assign one classification per child:

**Classification criteria:**

| Classification | Definition |
|----------------|------------|
| `on-theme`     | Child directly implements, fixes, or improves something explicitly named in the EPIC summary, or clearly supports the EPIC's stated goal |
| `tangential`   | Child touches related technology or systems but its stated goal does not map to any area named in the EPIC summary; could belong under a different EPIC |
| `off-theme`    | Child goal contradicts or is entirely unrelated to the EPIC summary; likely belongs under a sibling EPIC or no EPIC |

For each child, produce:
```
{id}: <classification> — <one sentence rationale>
```

Collect only `tangential` and `off-theme` results as `scope_drift_findings`.

---

## Step 6: Missing Coverage Analysis (LLM Pass)

**Skip this step if `ENABLE_SCOPE_DRIFT=false`.**

Parse the EPIC summary for named sub-areas, goals, or deliverable phrases. Look for:
- Quoted feature names, component names, or action phrases
- Numbered or bulleted sub-goals
- Sentences beginning with "will", "should", "must", or "covers"

For each identified sub-area, check whether any child's `## Summary` section covers it.
A child "covers" a sub-area when its summary mentions the same component, action, or
deliverable — exact wording not required, but semantic overlap must be clear.

Collect uncovered sub-areas as `missing_coverage_findings`:
```
{sub-area description}: no child issue covers this area
```

---

## Step 7: Closure Check (Pure)

Check all child statuses from `by_status`:

- **Ready to close** if `done_count == len(children)` (all children are `done` or
  `cancelled`) and `active_count == 0`.
- **Not ready** otherwise — note the count of still-active children.

---

## Step 8: Render Health Report

Output the following Markdown report. Use `N/A` for any section where data is empty.

```
================================================================================
EPIC HEALTH REPORT: EPIC_ID — EPIC title
================================================================================

**Progress**: done_count/total done (percent_done%) · active_count active · deferred_count deferred

### Stalled children  (> STALE_DAYS days without activity)

[For each stalled child]:
- CHILD_ID — CHILD_TITLE
  Status: child_status · Last activity: DATE (days_since_activity days ago)
  Recommendation: `ll-issues set-status CHILD_ID deferred` to park, or `/ll:manage-issue ... CHILD_ID` to resume. For bulk deferral of all stalled children, loop `ll-issues set-status CHILD_ID deferred` per child (this leaves the EPIC's own status untouched). Do NOT use `set-status EPIC_ID ... --cascade` for this — `--cascade` terminally closes the EPIC itself while only deferring children.

[If none]: No stalled children detected.

### Scope drift

[For each scope_drift_finding]:
- CHILD_ID [classification] — rationale
  Recommendation: reparent with `ll-issues` edit, or detach by removing `parent: EPIC_ID` from frontmatter

[If none / ENABLE_SCOPE_DRIFT=false]: No scope drift detected. [or: Skipped (--skip-drift).

### Missing coverage

[For each missing_coverage_finding]:
- "sub-area description" — no child issue covers this area
  Recommendation: `/ll:capture-issue` to create a child issue targeting this area

[If none / ENABLE_SCOPE_DRIFT=false]: No missing coverage detected. [or: Skipped (--skip-drift).]

### Closure recommendation

[If ready]: All children done or cancelled — recommend: `ll-issues set-status EPIC_ID done`. If any children are still open, resolve them individually first (`ll-issues set-status CHILD_ID done|cancelled`); do not use `--cascade`, which would only defer them while terminally closing the EPIC.
[If not ready]: Not ready (active_count active children)

### Related (not children)

[For each issue in related_not_children]:
- ISSUE_ID — ISSUE_TITLE (status: STATUS)

[If empty]: No cross-references (relates_to list is empty or all entries are also children).

================================================================================
```

After the banner, add a `## Recommendations` section that lists each runnable command
implied by the findings above, formatted as a numbered checklist:

```
## Recommendations

[N findings — or "No action needed." if all sections are clean]

1. (if stalled children exist) Defer or resume stalled children:
   `ll-issues set-status CHILD_ID deferred`  — park ENH-NNN (stalled N days). Bulk option: loop this command per stalled child; do not use `set-status EPIC_ID ... --cascade`, which terminally closes the EPIC while only deferring children.

2. (if scope drift) Reparent or detach drifted children:
   Review CHILD_ID frontmatter to update or remove `parent:` field

3. (if missing coverage) Capture new child issues for uncovered sub-areas:
   `/ll:capture-issue` — describe the missing sub-area and set `parent: EPIC_ID`

4. (if closure-ready) Mark EPIC done:
   `ll-issues set-status EPIC_ID done`
```

---

## Step 9: Guard Rails

- **Never write to any issue file.** This skill is read-only audit. All mutations are
  user-initiated follow-up commands shown in the Recommendations section.
- **`--skip-drift` mode**: Steps 5 and 6 are skipped; the scope-drift and missing-coverage
  sections display `Skipped (--skip-drift).` instead of findings.
- **Empty EPIC** (Step 2c exit): emit the early-exit message and stop — do not proceed to
  Steps 3–8.
- **EPIC not found** (Step 2a exit): emit the not-found error and stop.
- **Config defaults**: if `{{config.commands.review_epic.stale_days}}` is unavailable,
  use 14. If `{{config.commands.review_epic.enable_scope_drift_check}}` is unavailable,
  default to enabled.

---

## Usage Examples

```bash
# Full audit (stall + scope drift + missing coverage)
/ll:review-epic EPIC-42

# Structural-only audit (skip LLM scope-drift and missing-coverage passes)
/ll:review-epic EPIC-42 --skip-drift
```

---

## Output Evidence Contract (verbatim-output rule)

When this skill emits an audit finding, verdict, or scorecard, cite evidence
verbatim rather than re-summarizing — quoting is cheaper than paraphrasing and
keeps the audit auditable:

IMPORTANT: For each condition you evaluate:
1. State your verdict: Yes / No / Partial
2. Provide a VERBATIM quote from the output that supports your verdict (exact text, in quotes)
3. If you cannot quote specific text, your verdict is automatically No (or Partial if context suggests partial progress)

Do not assert a verdict without evidence. "The task appears complete" is not evidence.

Files in this skill

  • SKILL.md11.8 KB
  • agents/openai.yaml132 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…