---
name: issue-size-review
description: Use when asked to evaluate issue size, decompose large issues, or audit backlog complexity.
disable-model-invocation: true
model: sonnet
allowed-tools:
  - Read
  - Glob
  - Edit
  - Bash(ll-issues:*, git:*)
metadata:
  short-description: Use when asked to evaluate issue size, decompose large issues, or audit backlog 
---

# Issue Size Review Skill

This skill evaluates active issues for complexity and proposes decomposition for those unlikely to be completed in a single session.

## When to Activate

Proactively offer or invoke this skill when the user:

- Mentions an issue seems too large or complex
- Is doing sprint planning and wants manageable chunks
- Asks to audit or review issue sizes
- Mentions context running out during issue work
- Says "this issue is too big" or similar
- An issue fails `/ll:ready-issue` or `/ll:confidence-check` after two or more refinement passes — persistent readiness gaps often signal the issue needs decomposition rather than more research

## How to Use

Invoke this skill to review all active issues:

```
/ll:issue-size-review
```

## Arguments

$ARGUMENTS

Parse arguments for flags:

```bash
ISSUE_ID=""
AUTO_MODE=false
CHECK_MODE=false
SPRINT_NAME=""

# Auto-enable in automation contexts
if [[ "$ARGUMENTS" == *"--dangerously-skip-permissions"* ]] || [[ -n "${LL_NON_INTERACTIVE:-}" ]] || [[ -n "${DANGEROUSLY_SKIP_PERMISSIONS:-}" ]]; then AUTO_MODE=true; fi

# Explicit flags
if [[ "$ARGUMENTS" == *"--auto"* ]]; then AUTO_MODE=true; fi
if [[ "$ARGUMENTS" == *"--check"* ]]; then CHECK_MODE=true; AUTO_MODE=true; fi
if [[ "$ARGUMENTS" =~ --sprint[[:space:]]+([^[:space:]]+) ]]; then SPRINT_NAME="${BASH_REMATCH[1]}"; fi

# Extract issue ID (non-flag argument)
for token in $ARGUMENTS; do
    case "$token" in
        --*) ;; # skip flags
        *) ISSUE_ID="$token" ;;
    esac
done

# --sprint implies --auto
if [[ -n "$SPRINT_NAME" ]]; then AUTO_MODE=true; fi
```

- **issue_id** (optional): Specific issue ID to review (e.g., `ENH-179`)
- **flags** (optional):
  - `--auto` - Non-interactive mode: auto-decompose only Very Large issues (score ≥ 8) where decomposition is unambiguous. Skip Large issues (5-7) as ambiguous. Emit one status line per issue: `[ID] [action]: [summary]`
  - `--check` — Check-only mode for FSM loop evaluators. Run size scoring without decomposition, print `[ID] size: score N (oversized)` per issue scoring >= 5, exit 1 if any oversized, exit 0 if all pass. Implies `--auto`.
  - `--sprint <name>` — Scope the audit to only the issues listed in the named sprint definition (`.sprints/<name>.yaml`). Implies `--auto`. A summary header `Sprint: <name> (N issues)` is shown in the output.

## Workflow

The skill follows a 6-phase workflow:

### Phase 1: Discovery

**Sprint Mode** (`--sprint <name>`): When `SPRINT_NAME` is set, load issues from the sprint definition instead of scanning all active directories:

```bash
SPRINT_FILE=".sprints/${SPRINT_NAME}.yaml"
if [ ! -f "$SPRINT_FILE" ]; then
    echo "Error: Sprint '$SPRINT_NAME' not found at $SPRINT_FILE"
    exit 1
fi

# Read the sprint YAML with the Read tool; parse the issues: list (flat list of bare IDs)
# Resolve each ID to a file path using find:
declare -a ISSUE_FILES
for id in <sprint-issue-ids>; do
    FILE=$(ll-issues path "${id}" 2>/dev/null)
    if [ -n "$FILE" ]; then ISSUE_FILES+=("$FILE")
    else echo "Warning: Sprint issue $id not found (skipping)"; fi
done

echo "Sprint: $SPRINT_NAME (${#ISSUE_FILES[@]} issues)"
```

**Default (full backlog)**: Scan all active issues:

1. Use Glob to find all `.md` files in:
   - `{{config.issues.base_dir}}/bugs/`
   - `{{config.issues.base_dir}}/features/`
   - `{{config.issues.base_dir}}/enhancements/`
2. Read each issue file to extract content
3. Parse issue metadata (ID, type, priority, title)

### Phase 2: Size Assessment

Apply scoring heuristics to each issue:

| Criterion | Points | How to Detect |
|-----------|--------|---------------|
| File count | +2 | Count file paths (patterns like `src/`, `.py`, `.ts`, `.md`) mentioned in issue |
| Section complexity | +2 | "Proposed Solution" or "Implementation" sections >300 words |
| Multiple concerns | +3 | Multiple `##` subsections in solution, or phrases like "additionally", "also need to" |
| Dependency mentions | +2 | References to other issues (BUG-/FEAT-/ENH-/EPIC-) or "depends on", "blocked by" |
| Word count | +2 | >800 words total in issue file |

**Maximum score: 11 points**

Issues scoring **≥5 points** are candidates for decomposition.

### Phase 3: Frontmatter Write-back

Skip this phase when `CHECK_MODE=true`.

For each assessed issue, use the Edit tool to add or update `size: <label>` in the YAML frontmatter block. Apply the Size Thresholds table to derive the label from the score:

- Score 0-2 → `Small`
- Score 3-4 → `Medium`
- Score 5-7 → `Large`
- Score 8+ → `Very Large`

Write-back applies to **all** assessed issues (not just decomposition candidates). Perform the write-back per-issue inside the assessment loop immediately after each score is computed.

If the issue file has existing frontmatter (starts with `---`):
- Add or update the `size` field within the frontmatter block using the Edit tool
- Preserve all other existing fields

Example — if frontmatter is:
```yaml
---
id: ENH-123
priority: P2
---
```

Update to:
```yaml
---
id: ENH-123
priority: P2
size: Medium
---
```

If `size` already exists, replace its value with the new label.

If the issue file has no frontmatter, add one:
```yaml
---
size: Medium
---
```

After writing back, stage the file:
```bash
git add "<issue-file-path>"
```

### Phase 4: Decomposition Proposal

For each candidate issue:

1. Identify distinct sub-tasks or concerns by analyzing:
   - Separate sections in "Proposed Solution"
   - Different files/components mentioned
   - Distinct acceptance criteria
   - Logical boundaries between concerns

2. Propose 2-N focused child issues where each:
   - Has a clear, single responsibility
   - Is "independently shippable" — could produce its own PR with tests for whatever it introduces
   - Has testable completion criteria
   - Inherits appropriate priority and type

   **Never split by artifact type**: tests and docs for a child's new behavior belong in that child, not in a dedicated tests/docs child. The only exception: a test-only or doc-only issue for *already-shipped* code.

   **Never split wiring from implementation when TDD mode is configured**: If `config.commands.tdd_mode` is `true`, wiring (integration points, callers, registration hooks) belongs in the same child as the implementation that introduces it — the integration test that drives the wiring is part of the TDD cycle, and splitting it leaves the first child shippable only with mocks (risking mock/prod divergence) until the second merges. The only exception: wiring into a genuinely independent, separately-testable subsystem (e.g., a new transport protocol or storage backend) that qualifies as **independently shippable** on its own merits.

3. **Scope completeness check** — before drafting child content, enumerate every numbered step and every `###` subsection in the parent's "Proposed Solution" / "Implementation Steps". Map each to exactly one proposed child. If any parent step or subsection is not claimed by any child:
   - Emit a `⚠ SCOPE GAP` warning naming the uncovered step(s)
   - Either add another child to cover the gap, or explicitly note the step is being deferred (and explain why it is intentionally out of scope for this decomposition)
   - **Never proceed to execution with unresolved scope gaps** — missing scope silently drops work

4. **Ordering dependency analysis** — classify the execution pattern of the proposed children:
   - **Parallel**: children can be worked concurrently with no ordering constraints
   - **Partially ordered**: some children depend on others, but some parallelism is possible
   - **Strictly sequential**: every child must complete before the next can start

   Detect sequential ordering by looking for language in the parent such as "run after step N", "must complete before", "blocked by", numbered steps that explicitly build on each other, or shared infrastructure that one child builds and another consumes.

   If the pattern is **strictly sequential** AND the children share infrastructure or have tightly coupled scope, add a recommendation: `Consider keeping as one issue — strictly sequential children with shared scope offer no parallelism benefit and add tracking overhead.` Present this as a reason for the user to reconsider the split, but do not block the proposal.

5. Draft child issue structure:
   ```markdown
   ---
   id: [TYPE]-[NNN]
   priority: [P0-P5]
   type: [BUG|FEAT|ENH|EPIC]
   parent: [PARENT-ID]
   ---

   # [TYPE]-[NNN]: [Specific Title]

   ## Summary
   [Focused description from parent issue]

   ## Parent Issue
   Decomposed from [PARENT-ID]: [Parent Title]

   [Relevant sections from parent...]
   ```

### Phase 5: User Approval

#### Auto Mode Behavior

**When `AUTO_MODE` is true**: Skip the AskUserQuestion prompts below.

**Qualitative-skip guard (applies to both Large and Very Large issues)**: Before decomposing any issue, read its frontmatter for `score_ambiguity`, `score_complexity`, and `outcome_confidence`. If all three fields are present and non-zero, and `score_ambiguity ≥ 18` and `score_complexity ≥ 18`, the issue is structurally large but its confidence failure is qualitative (not a scope problem) — skip decomposition and emit:

`[ID] skipped: structural score N but outcome_confidence low is qualitative (ambiguity: A, complexity: C) — suggest /ll:refine-issue or /ll:wire-issue`

If any field is absent (confidence-check was never run on the issue), skip the guard entirely and fall through to normal behavior below.

**Normal auto behavior**: Auto-approve decomposition only for Very Large issues (score ≥ 8) where the decomposition is unambiguous (distinct sub-tasks with clear boundaries). Skip Large issues (score 5-7) as ambiguous — flag them in the output but do not decompose. Emit one status line per issue: `[ID] decomposed: N child issues` or `[ID] skipped: score X (ambiguous)`.

**Scope gap guard (auto and interactive)**: If the scope completeness check (Phase 4, step 3) found any unresolved gaps, do not execute the decomposition regardless of mode. In auto mode emit: `[ID] blocked: decomposition would lose scope — steps not covered: [list]. Review manually.` In interactive mode, present the gaps to the user and require them to either assign the uncovered steps to a child or explicitly mark them as intentionally deferred before approving.

#### Check Mode Behavior (--check)

**When `CHECK_MODE` is true**: Run size scoring only (no decomposition). For each issue scoring >= 5 (Large or Very Large), print `[ID] size: score N (oversized)`. After all issues scored, if any were oversized: print `N issues oversized`, then `exit 1`. If all pass: print `All issues pass size check`, then `exit 0`. This integrates with FSM `evaluate: type: exit_code` routing.

#### Interactive Mode (default)

For each decomposition proposal, use AskUserQuestion:

```yaml
questions:
  - question: "Decompose [ISSUE-ID] '[Title]' (score: X/11) into N smaller issues?"
    header: "[ISSUE-ID]"
    multiSelect: false
    options:
      - label: "Yes, decompose"
        description: "Create N child issues: [brief titles]"
      - label: "No, keep as-is"
        description: "Leave this issue intact"
```

Present proposals one at a time or batch (user preference).

### Phase 6: Execution

For each approved decomposition:

1. **Get next issue numbers**:
   ```bash
   ll-issues next-id
   ```
   Next numbers are the printed value, +1, +2, etc. for multiple issues.

2. **Create child issue files**:
   - Determine target directory based on type (bugs/, features/, enhancements/)
   - Generate filename: `P[priority]-[TYPE]-[NNN]-[slug].md`
   - Write issue content using the Phase 4 draft template; the frontmatter block **must** include `parent: [PARENT-ID]` (bare issue ID, e.g. `ENH-179`) to make the parent→child relationship machine-readable
   - For each child issue file created, append a session log entry:

```markdown
## Session Log
- `/ll:issue-size-review` - [ISO timestamp] - `[path to current session JSONL]`
```

To find the current session JSONL: look in `~/.claude/projects/` for the directory matching the current project (path encoded with dashes), find the most recently modified `.jsonl` file (excluding `agent-*`). If `## Session Log` already exists, append below the header. If not, add before `---` / `## Status` footer.

3. **Update and move parent issue**:
   Add resolution section to parent:
   ```markdown
   ---

   ## Resolution

   - **Status**: Decomposed
   - **Completed**: YYYY-MM-DD
   - **Reason**: Issue too large for single session

   ### Decomposed Into
   - [TYPE]-[NNN]: [Child title 1]
   - [TYPE]-[NNN]: [Child title 2]
   - [TYPE]-[NNN]: [Child title 3]
   ```

   Update parent issue status to done (frontmatter `status: done`) using the Edit tool.

   Before updating, append a session log entry to the parent issue file:

```markdown
## Session Log
- `/ll:issue-size-review` - [ISO timestamp] - `[path to current session JSONL]`
```

To find the current session JSONL: look in `~/.claude/projects/` for the directory matching the current project (path encoded with dashes), find the most recently modified `.jsonl` file (excluding `agent-*`). If `## Session Log` already exists, append below the header. If not, add before `---` / `## Status` footer.

4. **Stage all changes**:

   Stage only the files this review touched — the parent issue and each newly created
   child issue — by their explicit paths. Do **not** stage the whole `{{config.issues.base_dir}}/`
   directory: a directory-level `git add` recursively sweeps in unrelated untracked/modified
   files (e.g. draft issues from a concurrent skill), polluting the commit (BUG-1976).

   ```bash
   # Stage each reviewed/updated parent and each created child explicitly:
   git add "<parent-issue-file-path>"
   git add "<child-issue-file-path>"   # repeat for every child created in Phase 6
   ```

   Accumulate the paths as you create/modify them and stage them one by one. After staging,
   `git status` should show only the review's files staged.

## Output Format

```
================================================================================
ISSUE SIZE REVIEW                          [Sprint: <name> (N issues) | Full backlog]
================================================================================

## SUMMARY
- Issues scanned: N
- Large issues found: M (scoring ≥5)
- Decomposition candidates: K

## ASSESSMENT

### Small Issues (0-2 points)
- [ID]: [Title] (score: X)

### Medium Issues (3-4 points)
- [ID]: [Title] (score: X)

### Large Issues (5-7 points) - CANDIDATES
- [ID]: [Title] (score: X)
  Breakdown: files(+2), complexity(+2), concerns(+3)

### Very Large Issues (8+ points) - STRONG CANDIDATES
- [ID]: [Title] (score: X)
  Breakdown: [scoring details]

## PROPOSALS

### [ISSUE-ID]: [Title]
**Score**: X/11
**Breakdown**: [which criteria scored]
**Execution pattern**: [Parallel | Partially ordered | Strictly sequential]
**Scope coverage**: [✓ All parent steps covered | ⚠ Gaps: step N, subsection "X" unassigned]

**Proposed decomposition into N issues:**

1. **[TYPE]-[NNN]: [Child title 1]**
   - Scope: [What this child covers]
   - Covers: [which parent steps/subsections]
   - Files: [Which files this affects]

2. **[TYPE]-[NNN]: [Child title 2]**
   - Scope: [What this child covers]
   - Covers: [which parent steps/subsections]
   - Files: [Which files this affects]

**Rationale**: [Why this split makes sense]
**Note** (if strictly sequential): [parallelism warning if applicable]

[AskUserQuestion prompt]

---

## RESULTS

### Decomposed
- [PARENT-ID] → [CHILD-1], [CHILD-2], [CHILD-3]

### Declined
- [ID]: User chose to keep as-is

### Created Issues
- [TYPE]-[NNN]: [Title] (from [PARENT-ID])
- [TYPE]-[NNN]: [Title] (from [PARENT-ID])

### Moved to Completed
- [PARENT-ID]: [Title]

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

## Examples

| User Says | Action |
|-----------|--------|
| "This issue is too big" | Run issue size review on that specific issue |
| "Audit issue sizes" | Run full issue size review |
| "Break down large issues" | Run issue size review |
| "Sprint planning - need smaller tasks" | Run issue size review |
| "Review issue complexity" | Run issue size review |
| "Can we split ENH-179?" | Run issue size review targeting ENH-179 |
| "Review sizes non-interactively" | `/ll:issue-size-review --auto` |
| "Check if issues are sized for sprint" | `/ll:issue-size-review --check` |
| "Check sizes for a specific sprint" | `/ll:issue-size-review --sprint my-sprint` |

## Size Thresholds

| Score | Assessment | Recommendation |
|-------|------------|----------------|
| 0-2 | Small | No action needed - good size for single session |
| 3-4 | Medium | Borderline - may benefit from split if multiple concerns |
| 5-7 | Large | Recommend decomposition |
| 8+ | Very Large | Strongly recommend decomposition |

## Configuration

Uses project configuration from `.ll/ll-config.json`:

- `issues.base_dir` - Base directory for issues (default: `.issues`)
- `issues.categories` - Bug/feature/enhancement directory config
- Issue lifecycle state tracked via frontmatter `status` field

## Best Practices

### Good Decomposition

- Each child issue has **one clear goal**
- Children are **independently shippable** — each can produce a PR with tests for its own changes
- Children have **similar size** (avoid 1 large + 2 tiny)
- Children **preserve context** from parent (link back, include relevant details)

### Avoid

- Creating too many tiny issues (cognitive overhead)
- Splitting tightly-coupled concerns that should stay together
- **Losing scope when decomposing** — every numbered step and subsection in the parent must be explicitly assigned to a child or marked as intentionally deferred; unassigned scope is silently dropped work
- **Decomposing strictly sequential children with shared infrastructure** — if all children must run in order anyway and share modules, the tracking overhead outweighs the benefit; recommend keeping as one issue instead
- Losing context when decomposing (always reference parent)
- Creating circular dependencies between children
- Splitting tests or documentation into a dedicated child issue for newly-introduced behavior (they belong with the implementation)
- **Splitting wiring from the implementation that introduces it when `config.commands.tdd_mode` is `true`** — wiring is part of the TDD cycle; the integration test belongs with the wired feature, not in a follow-up issue. The only exception is wiring into a genuinely independent, separately-testable subsystem.

## Integration

After running issue size review:

- Review created child issues with `cat [path]`
- Validate with `/ll:ready-issue [ID]`
- Commit changes with `/ll:commit`
- Process with `/ll:manage-issue` or `/ll:create-sprint`
