# Sub-ticket / child-issue recursion (tracker sources)

Read this only when a fetched tracker ticket shows one or more child references
(okstra-brief-gen Step 1b sub-step 6). It defines how the child tree is discovered,
which briefs are emitted, and how re-runs stay idempotent.

## 1. Discover child references per tracker

- Linear: `children` / `subIssues` field.
- Jira: `subtasks` (or `issuelinks` with the `is parent of` relation).
- GitHub: `gh issue view --json title,body,comments,labels,state` only returns
  the issue body and comments — child relations are NOT in that JSON. Resolve
  children via two paths:
  1. **task-list parsing** — scan the fetched body and comments for markdown
     task-list checkboxes referencing other issues (`- [ ] #NNN`,
     `- [x] owner/repo#NNN`, `- [ ] https://github.com/...`). Capture each as a
     child candidate.
  2. **tracked / sub-issue GraphQL** — for GitHub Projects v2 "tracked issues" /
     sub-issue relations, query GraphQL explicitly, e.g.
     `gh api graphql -f query='query($o:String!,$r:String!,$n:Int!){repository(owner:$o,name:$r){issue(number:$n){trackedIssues(first:50){nodes{number repository{nameWithOwner}}} subIssues:trackedInIssues(first:50){nodes{number repository{nameWithOwner}}}}}}' -f o=<owner> -f r=<repo> -F n=<num>`.
     If the GraphQL request fails (auth scope missing, feature unavailable,
     network error), do NOT silently continue with full recursion: disable the
     "Full tree" option for this branch, default to `Parent only`, and surface
     the GraphQL failure to the user in one line so they can decide whether to
     paste child bodies manually.
- Notion: child pages / sub-pages.

## 2. Ask once at the top parent

If there is one or more child, `AskUserQuestion` (single-select):

- **Label**: `"This ticket has sub-tickets. How should the child tree be handled?"`
- **Options**:
  1. `Full tree` (recommended) — walk children, grandchildren, … via BFS/DFS
     and emit one brief per node.
  2. `Parent only` — single brief. Children are not fetched; their keys/URLs are
     recorded under Related Artifacts and as `parent-of` rows in Related Task
     Graph.
  3. `Selected` — multi-select the direct children; for each chosen child,
     apply option-1 policy recursively to that branch.

For options 1 / 3, recurse into Step 1b sub-steps 3–5 for every descendant. No
depth limit. Maintain a visited set of `<tracker>:<ticket-id>` to prevent
cycles; on revisit, do not emit a new brief — only add a link back to the
existing brief.

## 3. Visited-set across re-runs (rebuild from disk)

The in-memory visited set is per-run. When this skill runs again over a tree
that already has briefs on disk, reseed the visited set by scanning
`<PROJECT_ROOT>/.okstra/briefs/<task-group>/` recursively and reading each
brief's frontmatter `ticket-id` + `source-type`. Reseed precedence:

1. On-disk frontmatter (`ticket-id` ≠ "") populates visited entries as
   `<source-type>:<ticket-id>` — these win.
2. Step 2c collision policy (`Skip` / `Append timestamp` / `Overwrite`) applies
   to any node whose path is already taken.
3. The fresh in-memory walk fills any gaps for tickets not yet on disk.

A ticket present on disk is NOT refetched unless the user explicitly answers
`Overwrite` for that path.

## 4. Per-node output

- Each descendant's brief file at depth N is created under
  `<task-group>/<sub/ nested N times>/<ticket-id>-<file-title>.md` per Step 2b.
- Each node's `Related Artifacts` lists, bidirectionally:
  - the parent brief's relative path (`../<ticket-id>-<file-title>.md`)
  - all direct children briefs' relative paths
    (`sub/<ticket-id>-<file-title>.md`)
  (Non-direct ancestors/descendants are tracked via the frontmatter
  `parent-id` chain.)
- Each generated brief's `Related Task Graph` contains the same **full split
  topology**, not only the current node's direct edges, so downstream phases
  can start from any child brief without losing order or dependency context.
  One row per source-backed edge with columns
  `From | Relation | To | Direction | Source | Impact`:
  - `From` / `To` use task key, brief id, tracker id, or URL; edge direction is
    `From` → `To`.
  - `Relation` is one of `parent-of`, `child-of`, `depends-on`, `blocks`,
    `blocked-by`, `follow-up-of`, `split-from`, `duplicates`, `related-to`.
  - `Direction` is `directed` for parentage/ordering relations, `undirected`
    for `duplicates` / `related-to`.
  - `Source` names the evidence for the edge: tracker linked issue, markdown
    task-list checkbox, reporter statement, manual split, or prior okstra task.
  - `Impact` states what the next phase must preserve (e.g. "run after API
    contract", "avoid duplicate implementation").
  Do not invent graph edges from filename similarity or topic overlap; if a
  relation is unclear, use `related-to` + `undirected` only when the source
  explicitly says the items are related.
