---
name: epic
description: Decompose a vision or umbrella issue into linked child issues via one batch create.
---

# /epic — decompose an umbrella into children

Turn one large item — a vision issue, an epic, an umbrella issue — into a set of native sub-issues in a
single pre-validated batch, each already linked to its parent. This is the slice-and-fan-out act done by
hand today: draft the children, validate them all, create them, confirm the tree.

## Opening branch — create a new umbrella when none exists

When the request is a product intent rather than an existing issue, gather the intent first, read the
repo's live issue templates, draft the umbrella from the matching feature/task template, and show that
draft for the user's go. Only then create one issue with the `epic` label and an explicit Priority field;
re-read it from the board to prove the body, label, and priority landed. Stop there unless the user also
asked for and approved child decomposition.

## Step 0 — load the umbrella whole

Read the parent end-to-end before slicing — body **and every comment**, treating later comments as
superseding:

```bash
mmi-cli oracle board show <owner/repo#N>       # status, body, and every comment for the umbrella
```

Slice by deliverable, not by phase: each child must be independently claimable and land in its own PR.
Keep the parent as the umbrella — do not close it until the last child merges.

## Step 1 — draft the batch file

Before drafting, inspect the umbrella's existing children:

```bash
mmi-cli oracle issue children <owner/repo#N>        # each child: number/title/state/repo/boardStatus/linkedPrs
```

Write a JSON array (one object per child) to a scratch path under `.jerv/tmp/`, named uniquely for this
epic — `.jerv/tmp/epic-<parentNumber>-children.json` (never reuse the file between epics: a previous
epic's leftover batch collides with the new rows, silently on an unguarded host). Each row:

```json
[
  { "type": "task", "title": "C1: extract the shared frame", "body": "…", "priority": "high",
    "surface": "cli", "parent": "owner/repo#42" },
  { "type": "feature", "title": "C2: wire the new command", "body": "…", "priority": "medium",
    "parent": "owner/repo#42" }
]
```

- `type` — `bug` · `feature` · `task` (required; maps to the repo's issue-template label).
- `title` — required, non-empty; keep it a short, greppable deliverable.
- `parent` — the umbrella ref (`N` or `#N` as a string, a bare JSON number, `owner/repo#N`, or
  URL); links each child as a native sub-issue on create, so you do not need a separate `link-child`
  pass. Cross-repo is fine (a Hub umbrella can parent product-repo children).
- `priority` — `urgent|high|medium|low` (defaults to `medium`); sets the board Priority **field**.
- `repo` — optional per-row target repo (`owner/repo`); defaults to `--repo`, then the current repo.
  This is how one batch fans children out across repos under a single umbrella.
- `surface` — required when the target repo uses `surface:*` taxonomy; accepts the value with or without
  the prefix. A top-level `--surface <value>` may supply the default for every applicable row.

The batch **pre-validates every row before creating any** — one bad row fails the whole set with an
aggregate error, so you fix all of them once. It warns when a row title closely matches an existing open
child of the same parent; inspect that warning before creating a duplicate. Get the user's go on the drafted
set before writing.

## Step 2 — create the children

```bash
mmi-cli oracle issue create --type task --batch .jerv/tmp/epic-42-children.json --surface cli
```

`--type` is required by the create command but each row's own `type` governs; pass any valid type as the
placeholder. It prints per-row `{number,url}` results and fails loud on any misfire. Add
`--idempotency-key <key>` when a retried loop must not duplicate.

When creating through `mmi-cli vault secrets use GH_TOKEN -- …`, the `GH_TOKEN` environment override can
disrupt Hub session discovery and print `board attach skipped — no Hub session token` for batch rows. That
warning can appear when placement is correct, but it is not harmless or proof of placement: final verification
must prove every intended child is linked and on the board as Todo with `mmi-cli oracle issue children
<owner/repo#N> --json`. `boardStatus: null` is **inconclusive** (the children walk can miss org-board
Status that `board show` still sees) — it is not proof the child is off the board. A non-null Status
(Todo / In Progress / …) is the placement proof; if Status stays null after this command, investigate
with `board show` rather than treating null as "not on board".

The idempotency lookup is find-before-create, not an atomic reservation. Run one batch writer at a time:
never submit concurrent creates with the same key. Size the calling command's wall-clock budget for the
whole sequential batch. If the command times out, its outcome is unknown — confirm the process has ended,
wait for writes to quiesce, inspect the children, then retry the missing rows with the same key.

## Step 3 — confirm the tree

```bash
mmi-cli oracle issue children <owner/repo#N> --json # each child: number/title/state/repo/assignee/boardStatus/linkedPrs
```

This JSON is the final placement authority: verify every intended child is linked and on the board as Todo
(`boardStatus` names Todo). `boardStatus: null` is inconclusive, not "not on board".
If it does not prove that placement, investigate and correct the misfire. To link a child that already existed (not
part of the batch), use the inverse-friendly single link:

```bash
mmi-cli oracle issue link-child <parent> <child>
```

Each child is now a parallel item — fan them out one worktree + PR each. On Windows, set
`git config core.longpaths true` on the main checkout before the first `git worktree add` (#5499). When
several linked worktrees belong to the same clone, serialize `git branch -m` / other ref renames (or retry
serially on `.tmp-renamed-log`) so parallel agents do not race the shared ref log (#5485). **When the last
child merges, close the umbrella** (its board `Done` follows automatically).

## Notes

- Native sub-issues, not a body task-list: the parent renders a sub-issue checklist and each child renders
  its parent — nothing to hand-maintain.
- Never free-type children with raw `gh issue create` — the batch path carries validation, board attach,
  and structured output.

## Retro — one check before you finish
Before your final report, answer one question honestly: did **this skill's own instructions** misfire
this run — ambiguous wording, a misleading message, or an environment failure it should have warned
about? (Process only — never the user's code or task; e.g. a batch schema that rejected a valid row, or a
link that attached to the wrong parent.) If yes, file **one** lesson and move on; a clean run is silent
(hard cap: one per run). It lands on the owning board (deduped) and is fixed only via a reviewed PR — never
edit the skill live; the retro is advisory, so if the call fails, note it and continue. File through MMI:
`mmi-cli learning skill-lesson --skill epic --title "<what misfired>" --body "<what; evidence; proposed amendment>"`
