# get_why

get_why answers why code is shaped this way. Search decisions in plain language, anchor them to a file, or read decision health, with git history as fallback.

The decision intelligence layer in one tool. `get_why` answers
*"why is this code shaped this way?"*, pulling from the architectural
decision records, recorded episodes, the wiki, and (when nothing's
recorded) the git history and rationale comments themselves. How decisions are
captured is on [Architectural decisions](https://docs.repowise.dev/intelligence/decisions), and the
terminal equivalent is [`repowise why`](https://docs.repowise.dev/cli/query#repowise-why-query). The
other tools are on [MCP tools](https://docs.repowise.dev/mcp/overview).

The thing to understand before reading a response: **acceptance is the
only authority.** A record binds you because somebody accepted it in a
recorded event, not because a column says `active`. Path mode sorts
every record that names your file into one of three lanes on exactly
that test.

## When to call

- **Before architectural changes**: check whether an accepted decision
  governs what you're about to touch.
- **During design reviews**: pull the health dashboard to find records
  whose code has moved, and hotspots nobody has decided anything about.
- **On code with no recorded decisions**: the git-archaeology fallback
  reconstructs the origin story from significant commits.
- **Following up on an id**: an evidence ref another response emitted
  resolves directly, with no relevance search in between.

## Parameters

| Name | Type | Default | Description |
|---|---|---|---|
| `query` | string |  | Natural-language question, a file/module path, or omit entirely for the health dashboard. |
| `targets` | string[] |  | File paths to anchor the search. With no query, the targets are the question. |
| `repo` | string |  | Repository alias. Pass "all" to search decisions across the whole workspace. Not supported with id or with the dashboard. |
| `id` | string |  | A decision id or an ev_... evidence id previously emitted by get_why. Resolves it directly, skipping relevance search. |
| `reference` | object |  | An emitted evidence reference object. Pass it back unchanged and both its id and its repository scope are honoured. |

## The three lanes

Path mode returns the records that name a file split by who agreed to
them. The lane, not the status, is what tells you whether a record
binds you.

| Lane | What it holds | How to read it |
|---|---|---|
| `decisions` | Accepted records that still govern | Constraints. Follow them |
| `candidates` | Records something inferred and nobody has agreed to | Hints. A `candidates_note` says so too, and names the `repowise decision confirm` command that would promote one |
| `history` | Accepted once, superseded or dismissed since | History. Useful for "this was decided and then replaced", never a rule |

Nothing produces an acceptance except an explicit
`repowise decision confirm` or a committed ADR that says it is
accepted. A candidate that has recurred across fifty sessions is still
a candidate.

Each accepted record carries a `currency`, which is what replaced
status as the truth:

| `currency` | Meaning |
|---|---|
| `active` | Accepted, and still describes the code |
| `needs_review` | Accepted, and the files it names have moved. It still binds |
| `uncheckable` | Accepted, but it names nothing the repository can be asked about |
| `superseded` | Replaced by a later decision through an explicit lineage edge |
| `dismissed` | Authority withdrawn, kept for history |

`active` and `needs_review` are the two that govern. A candidate
carries `review_state: "open"` and no `currency` at all. Every row also
carries an `authority`: `accepted`, `candidate`, or `withdrawn`.

> **Warning**
>
> Do not read the lane off `status`. That column is a projection kept in
> step for readers that predate the split, and a record can carry
> `status: "active"` with no acceptance behind it.

## Returns

`answer_basis` names the strongest lane the whole response rests on:
`decision`, `episode`, `rationale`, `archaeology`, `documentation`, or
`candidate`. Only `decision` is a ruling. `candidate` is the weakest,
and means nothing cleared the bar. It is absent on the dashboard, which
is orientation instead of an answer.

### Mode 1: Search (`query` is a question)

- `mode: "search"`, `query`
- `decisions`: up to 3 merged hits (keyword + semantic, deduped)
- `related_documentation`: top 3 wiki pages mentioning the query
- `target_context`: *(if `targets` provided)* per-target governing
  decisions, candidate decisions, origin story, and git archaeology
- `code_rationale`: *(no decisions matched)* rationale comments mined
  live from the targets' source, or from the whole repository when no
  targets were given
- `git_archaeology`: *(no decision and no rationale comment matched)* the
  commits whose messages carry the question's terms. Labelled as history,
  never presented as a decision
- `episodes`: dated facts recorded about the query (or the targets,
  when given), when any are bound

### Mode 2: Path (`query` is a file path)

- `mode: "path"`, `path`
- `decisions`: the accepted, governing lane, ranked before the cap of 8
  so the 8 that survive are the 8 that govern. `decisions_total` and
  `decisions_omitted` appear when more matched. The top-ranked record
  also carries a `still_true` sentence when git can speak to whether it
  holds, and only the top one: the check costs a subprocess
- `candidates` and `candidates_note`, `history` and `history_note`: the
  other two lanes, each capped at three rows
- `origin_story`: when and why the file was created, by whom, recent
  change patterns. Built from every lane, since a commit matching a
  candidate's title still explains the file. On a directory
  (`scope: "directory"`) it is built from the files under it: how many
  have history, the first and last commit dates, and their key commits
  once each, with no commit count or owner claimed
- `alignment`: coverage counts, scored over every matching record
  instead of the capped 8
- `git_archaeology`: present whenever no accepted decision governs the
  path, beside whatever candidates or history were found
- `code_rationale`: rationale comments mined live from the source when no
  accepted decision governs the path
- `episodes`: dated facts recorded about this file, when any are bound.
  Additive, not a fallback

### Reading `alignment`

Four counts sum to `governing_count`, which is every record naming the
file:

- `active_count`: what actually governs it
- `deprecated_count`: accepted and withdrawn since
- `uncheckable_count`: accepted but naming nothing
- `candidate_count`: merely awaiting review

`score` is derived from `active_count` alone, so a file with
`active_count: 0` is ungoverned however many candidates name it.
`stale_count` is the subset of governing records sitting at
`needs_review`, and `sibling_coverage` is the share of comparable files
in the same directory that an accepted decision covers.

### Mode 3: Health dashboard (no `query`, no `targets`)

- `mode: "health"`
- `summary`: a one-line count string
- `counts`: structured counts by lane
- `stale_decisions`: ranked by staleness, worst first
- `proposed_awaiting_review`: records nobody has accepted, ranked by
  confidence
- `ungoverned_hotspots`: high-churn files no accepted decision names
- `conflicts`: decisions that disagree over the same files
- `retired_decisions`: dismissed, deprecated or superseded records, each
  with its `lane`
- `unscoped_decisions`: accepted records that name no file

The counts are computed off the acceptance, not off the status column,
so `active` here means "somebody accepted this" and `proposed` means
"nobody has".

### Mode 4: Reference lookup (`id` or `reference`)

- `mode: "reference"`: the exact evidence and its supporting decision,
  in one call and with no relevance search

### Mode 5: Workspace search (`repo="all"`)

- `mode: "search"`, `workspace: true`
- `decisions`: keyword results merged across repos, capped at 5

## Example

```python
get_why()                                     # health dashboard
get_why("why is auth using JWT?")             # search
get_why("src/auth/service.py")                # path mode
get_why(targets=["src/cache/service.py"])     # the targets are the question
get_why(id="ev_8f21c4")                       # resolve an emitted ref
get_why("redis integration", repo="all")      # workspace search
```

## Things worth knowing

- **Path detection**: natural language wins first. A query ending in
  `?`, starting with a question word ("why", "how", ...), or reading as
  a sentence (4+ words including a question word) always routes to
  search. Otherwise a query containing a path separator or ending in a
  recognized code extension routes to path mode.
- **Named targets with no query** are the question. The call answers
  about those files instead of falling through to the dashboard.
- **A missing lane can mean a tight budget.** `candidates` and
  `history` shed first under response-budget pressure, so their absence
  is not proof they were empty. `answer_basis` is re-derived after
  shedding, so it never names a lane the budget emptied.
- **Candidates reach no other tool.** `get_overview`, `get_risk`
  directives and `get_answer` serve accepted records only. A candidate
  never arrives anywhere as an instruction.
- **Evidence ids are shared, not corroborating.** Evidence-bearing rows
  carry a `provenance` and self-contained `evidence_refs`. Two rows
  with matching ids rest on the same evidence, which is not two
  independent confirmations.
- **Git-archaeology fallback** runs when no accepted decision governs a
  file: significant commits from the file itself, cross-file references
  in other files' commits, then live `git log --follow` and
  `git log --grep` under a 15-second ceiling.
- **Workspace mode (`repo="all"`)** queries the repos concurrently and
  names any that failed in `repos_failed` and any whose index is behind
  in `repos_stale`. It uses keyword search across all
  repos' decisions, capped at 5 merged results, and is unsupported for
  the dashboard and for `id` lookups.

> **Note**
>
> Decisions are linked to graph nodes, so when the agent edits a file
> `get_why` will surface the records that govern it without you having to
> ask. This is what keeps the *why* in the codebase when the original
> author leaves.
