# Architectural decisions

Architectural decision records linked to the files they govern, captured by hand, mined from git history or inline markers, and served to agents via get_why.

Most documentation rots because the *why* lives in the original
author's head. Repowise treats architectural decisions as first-class
data: captured, linked to the files they govern, tracked for
staleness, and surfaced to your AI agent via [`get_why`](https://docs.repowise.dev/mcp/get-why).

## What's a decision

A decision is a structured record with:

- **Title**: short summary.
- **Context**: what was the situation.
- **Decision**: what was chosen.
- **Rationale**: why.
- **Alternatives**: what was rejected.
- **Consequences**: what it implies going forward.
- **Affected files**: the graph nodes the decision governs.
- **Kind**: `architectural` (a claim about the code; a diff can violate it,
  the default) or `agreement` (a working-conduct rule, like commit
  attribution or which checkout to touch, with no file scope). Only the
  sessions lane produces `agreement` automatically; `repowise decision add
  --kind agreement` records one by hand. A diff cannot violate a rule that
  names no code, so an agreement is never checked against the graph.
- **Tags**: for cross-cutting concerns (security, performance, etc.).
- **Status**: `proposed`, `active`, `deprecated`, `superseded`, `dismissed`.
- **Verification**: `verified`, `fuzzy`, or `unverified`, from an
  anti-hallucination gate that checks each claim against a literal
  source span rather than trusting the extraction.
- **Source**: one of the ten capture channels below: five mined at
  index time (ADR files, inline markers, git archaeology, PR bodies,
  code comments), the opt-in `conventions` source, two session sources
  (`session`, `session_discovery`), an agent's own `agent` proposals and
  manual `cli` capture.

## Ten sources: index time, sessions, conventions and the CLI

Beyond manual capture via the CLI, repowise mines decisions
automatically from five sources at index time: ADR files, inline
markers, git archaeology, PR bodies and code comments. ADR files and
inline markers need no LLM key; commit, PR and comment mining need a
model and are skipped with a stated reason when none is configured.
Agent sessions, session discovery and conventions are off by default.
A failure in one source never blocks the others.

CHANGELOG and README files are not mined. Numbered ADR folders are found at
any depth.

### The CLI

Capture a decision interactively in ~90 seconds:

```bash
repowise decision add
```

See [`decision` CLI commands](https://docs.repowise.dev/cli/decision) for the full
subcommand list.

With `decisions.capture_prompt` on (the default), a successful `git commit`
whose message carries two or more decision signals prompts the agent, once per
session, to run `repowise decision add --agent SLUG`. Never on a merge or
rebase, and never for a commit a record already cites. It only proposes: what
the agent records lands as a candidate with source `agent`, ranked beside
session-mined candidates, and accepting it needs an evidence reference.
`repowise decision config capture-prompt --off` turns it off for one repository.

### Inline markers in source

Drop a marker comment in the file it governs:

```python
# WHY: JWT chosen over sessions; API must be stateless for k8s scaling
# DECISION: All external API calls wrapped in CircuitBreaker after Q3 outages
# TRADEOFF: Accepted eventual consistency in preferences for write throughput
```

These get extracted at index time, linked to the file they live in,
and treated as `active` decisions.

### Git archaeology

Repowise mines significant commit messages and proposes decisions
automatically. Run `repowise decision list --proposed` to review and
either accept them (`repowise decision confirm <id>`) or dismiss
them (`repowise decision dismiss <id>`).

### The other three sources

ADR files (Nygard/MADR format), PR and squash-commit bodies, and code
comments (block comments and docstrings on high-centrality files that
carry a rationale cue, such as "because" or "trade-off") are all mined
the same way: each candidate is checked against a literal source
passage before it's kept, so a decision is only ever `verified` when
the evidence actually says so.

## Verify, don't invent

Every mined decision carries a **verification status**: `verified`
(the claim traces to a literal quote in the source), `fuzzy` (related
evidence exists but doesn't match exactly), or `unverified` (kept, but
flagged rather than silently dropped or overwritten). Repowise never
fabricates a rejection it can't justify.

## Acceptance is the only authority

Mining a decision does not make it govern anything. A record only reaches your
agent, scores path alignment, or lands in the generated `CLAUDE.md` once
a person or a signed agent accepts it. Recurrence, confidence and a model's verdict are evidence
that a candidate is worth reading; none of them is an acceptance. The single
exception is a committed ADR that says `accepted` or `approved` and names what
it governs: that document accepts its own decision, with the file recorded as
the accepter.

Acceptance is a recorded event rather than a status somebody set. It carries
the decision text, a reason, a scope, an evidence reference, and who accepted
it, and a record missing any of those is refused rather than stored with a blank filled in. The
refusal names the gap:

```text
$ repowise decision confirm 4b6ddc58
Cannot accept 4b6ddc58: no scope: name the files or modules it governs
Supply the missing parts with --decision, --reason, --scope or --evidence.

$ repowise decision confirm 4b6ddc58 --scope packages/core/src/repowise/core/ingestion
Decision 4b6ddc58 accepted (governing)
```

`confirm` and `dismiss` both take a list of ids. Every id in a batch goes
through the same contract, and each is applied on its own, so one refusal
commits the rest and reports itself instead of ending the run. `--preview` runs
the batch through the contract and rolls it back. Accepting from the Decisions
page goes through the same contract: a candidate missing a reason, a scope or an
evidence reference has its Accept disabled with the gap named beside it, rather
than failing after the click.

Mining quotes the decision verbatim from its source, so a mined candidate
carries the exact words that made it. A record with no decision text, mined or
typed, is refused until `confirm --decision` supplies it.

An agent reviews and accepts by signing as itself
(`decision confirm ID --agent claude_code --session "$SESSION_ID"`), under the
same contract as a person. This is on by default; the dashboard badges any
acceptance a person did not sign, and the generated `CLAUDE.md` marks it
`[accepted by SLUG, not a person]`. `repowise decision config agent-acceptance --off`
keeps granting to people while agents still propose and withdraw.

The acceptance log is append-only. Re-accepting after review, sending a decision
back to review, superseding it and withdrawing it each add a row, so the history
of who granted and withdrew authority survives every later action.

A decision's id is derived from the identity it dedupes on, so the same decision
found twice is the same row. Merging and superseding retire ids that may already
be written down somewhere, so both leave an alias and the retired id keeps
resolving.

## Linked to the graph, connected to each other

Every decision is linked to the graph nodes it governs. When your
agent edits a file, [`get_why`](https://docs.repowise.dev/mcp/get-why) automatically
surfaces the decisions that touch it, without you having to remember
to ask.

Decisions can also connect to each other by typed edges: `supersedes` (one
decision replaces another), `refines` (narrows or extends without
reversing it), `relates_to` (same topic, no ordering implied), and
`conflicts_with` (two active decisions that contradict, with neither
clearly winning, a governance smell surfaced in code health).

Lineage comes only from two explicit sources: `repowise decision deprecate
ID --superseded-by ID2`, where the successor must be a decision in the same
repository, and `repowise update`, which marks a decision `superseded` when a
later commit reverses it. Repowise does not infer `supersedes` or
`conflicts_with` edges from text similarity, because similarity does not
reliably scope to a shared topic.

## Mined from your sessions

Beyond the index-time sources, repowise reads your own Claude Code
transcripts for the corrections and conventions you actually enforce
("new endpoints go through the auth middleware", "use the shared HTTP
client, not raw requests"), and turns the durable, repeated ones into
tracked decisions. Deterministic gates decide what's worth keeping
before a single batched LLM pass writes them up, so a one-off nudge
never becomes a permanent rule. Repo-wide rules with no specific file
(a project-wide convention) are kept too, and apply everywhere.

Session mining is off by default. The `balanced`, `full`, and `local_only`
presets turn it on; the plain default preset does not. Turn it on directly
with:

```bash
repowise decision source set session --on
```

See [Configuration](https://docs.repowise.dev/installation/configuration#the-decisions-block)
for the full `decisions:` block and every preset.

## Delivered where it matters

Decisions reach the agent through the [proactive hooks](https://docs.repowise.dev/intelligence/hooks),
no LLM and no round-trip:

- **At session start**, the decisions relevant to what this session is
  about to touch land in a compact block, scored against the working
  set. Relevance or silence: nothing is injected just for being
  high-confidence.
- **At edit time**, when the agent edits a file a decision governs, it
  gets a one-line "governed by" notice with the rationale.

And it learns: every injected decision is recorded locally, and on the
next `repowise update` the session miner checks whether the guidance was
followed or contradicted by your corrections, relaxing or bumping the
decision's staleness, so guidance that stops being true stops being
injected.

## Staleness and health

Decisions decay two ways. If a file's `last_commit_at` moves forward but the
decision's `last_update_at` doesn't, repowise flags it stale. And if every
file a decision names is gone at HEAD (checked across renames), it also reads
as stale: nothing was superseded, the code it described just isn't there
any more. Neither is a retirement; if a named file comes back, the flag
clears. A record written before its evidence commits landed counts from the
commit (or squash) that landed them, so landing a branch never makes its own
decision stale.

A decision retires outright (not just stale) when every commit
it rests on has been reverted: the code changed, so does the record.

Run:

```bash
repowise decision health
```

Or, agent-facing:

```python
get_why()  # no arguments → health dashboard
```

You'll see stale decisions, proposed-awaiting-review, and ungoverned
hotspots (high-churn files with no decisions on file).

## Decisions in change evidence

Accepted decisions are part of every change's evidence:

- The `decisions_crossed` item on each
  [Change Record](https://docs.repowise.dev/intelligence/change-records#the-evidence-checklist)
  lists the accepted decisions that govern the changed files.
- A [change policy](https://docs.repowise.dev/intelligence/change-policy) rule can test
  `decision_crossed: true`, for example to require an owner's review.
- `repowise update` checks decision evolution against the commits that update
  brought in, in parallel under the generation `--concurrency` setting, so the
  decisions phase takes seconds on an active repository.

## When there's no decision

[`get_why`](https://docs.repowise.dev/mcp/get-why) treats a path as governed only by a decision
whose scope covers it exactly. When none does, it falls back to rationale
comments (ranked by how much of the path they cover) and up to five history
commits for that path, labelled as what they are and never presented as a
decision. An index with no accepted decisions, or a path it does not have,
says so.

## Workspace decisions

In a workspace, decisions are per-repo by default. Pass `repo="all"`
to [`get_why`](https://docs.repowise.dev/mcp/get-why) to search across the workspace.

> **Note**
>
> **The senior-engineer-leaving problem.** When the person who knew
> why it was built that way leaves, that knowledge usually leaves with
> them. Decision intelligence is the layer that keeps it in the
> codebase.
