# Related-issue context at intake

A development sub-task is often filed with no description of its own. The
requirement sits on the parent, and the rest of the picture  -  the analysis, the
test scope  -  sits on the sibling sub-tasks beside it. The fetcher already read
the parent; it did not read the siblings, so a board that keeps its analysis in a
separate sub-task handed the pipeline an empty task.

`issue-fetcher.sh` now reads those siblings and exposes them as
`descriptor.relatedIssues[]`.

## What it costs

One search request, and only when the issue has a parent:

```
GET /rest/api/2/search?jql=parent="<PARENT>"&fields=summary,issuetype,status,description&maxResults=20
```

An issue with no parent has no siblings, so the lookup is skipped entirely and
that run pays nothing. The count does not grow with the number of siblings: the
issue's own `subtasks` field lists ITS children rather than its siblings, and the
parent's lists siblings without their descriptions, so either of those shapes
would cost one GET per sibling. `smoke-jira-context.sh` counts the requests, so
this stays a measurement rather than a claim.

The request lands before the maturity verdict reaches the user, which is one
extra round trip on the intake path. `jiraContext.enabled: false` is the way out
when latency matters more than the context.

## Shape

```json
"relatedIssues": [
  {
    "key": "PROJ-1002",
    "relation": "sibling",
    "type": "Sub-task",
    "status": "Done",
    "summary": "...",
    "description": "...",
    "truncated": false
  }
]
```

`type` is carried through verbatim from Jira and is never matched against in
code. Boards name their sub-task types differently and the query does not need
to know: `parent = <key>` returns all of them.

Siblings that carry a description are ordered first, so `maxItems` drops the
empty ones rather than the useful ones. An empty sibling is still listed  -  its
key, type and status are information  -  with an empty `description`.

## Settings

`prefs.global.jiraContext`:

| Key | Default | Effect |
|---|---|---|
| `enabled` | `true` | `false` skips the search entirely |
| `maxItems` | `6` | Hard cap; `0` behaves like `enabled: false` |
| `maxCharsPerItem` | `1200` | Per-sibling truncation, marked `truncated: true` |

Worst case payload is `maxItems x maxCharsPerItem`, next to the 4000-character
cap on the issue's own description.

## Maturity

One new code, `description_empty_sibling_available`, and it fires narrowly: the
issue's own description is empty, the parent's is empty or unreachable, AND a
sibling carries content. Today that combination produces the hard
`description_empty` blocker; it becomes a warning instead, the same downgrade
`description_empty_parent_available` already makes one level up.

The narrowness is deliberate. `score` is `100 - 40*blockers - 10*warnings`, and
the picker continues silently only at `score >= 90`, so a warning that fired
whenever an issue merely HAD siblings would cost ten points on every parented
issue and turn a silent intake into a question across the board.

## Where it goes

The descriptor is carried verbatim into the picker state as `issueRef`, and the
bridge copies `relatedIssues` onto `agent-state.json` when it is non-empty.

Persisting it is the point: `/multi-agent:resume` rebuilds context from durable
artefacts and never from the conversation, so an intake-only enrichment would be
gone by the first resume. The task's own `description` and `maturity` are still
not persisted, which is a known asymmetry rather than an oversight  -  this
change did not widen the bridge beyond the one field it needed.

Sibling text is added as its own labelled block. It never replaces the working
description the way an accepted `parentDescription` does: substituting it would
erase the provenance that Phase 2's parent-story scope-drift check reads.

## Not included

Following the parent's `issuelinks` in a second search. A linked bug can be the
whole requirement while a linked test-execution record is noise, and the fetcher
cannot tell them apart, so the scope stops at sub-tasks. There is no setting for
it: a switch that does nothing is worse than an absent one.
