Skip to main content
whyfile turns the tribal “why is the code like this?” into a layer that is queryable, PR-reviewable, and survives turnover. Three jobs a team gets for free (all LLM-free, $0 at query time):
  • Onboard. A new hire runs whyfile explain <file> instead of interrupting a senior.
  • Review. whyfile changed --base main surfaces the intent a PR touches, so a reviewer sees the constraints a change is bound by; whyfile review-context --base main turns that into a brief an external reviewer like Codex can act on directly.
  • Retain. whyfile coverage shows which code has recorded intent and which is dark, and whyfile digest reports what was decided since a checkpoint.

The PR check

Maps each changed file to the intent that governs it (constraints and trade-offs first). It is informational by default (exit 0). Add --fail-on-constraint to turn it into a soft gate that exits 5 when a PR touches a file bound by a constraint, so the constraint gets a reviewer’s acknowledgement.

Coverage and digest

coverage reports the golden fraction beside the coverage numbers: the share of intent that is trusted evidence (human-written: authored decision records, captured live decisions, and attested commit trailers / # why: comments; the numerator grows further as the confirmed tier lands) over total intent. It is partitioned by evidence class and never blended, so reconstructed intent is shown separately and never inflates the trusted count. Alongside it, intent_debt reports dark files and orphaned intent (intent that explains no code); stale decisions come from drift and disputes from the audit phase, reported as null here rather than a misleading zero. The attested tier is the zero-workflow capture channel: on every pipeline run (unless --no-attest), commit trailers (Why:/Decision:) and # why: code comments are ingested deterministically as provenance: attested intent nodes, anchored to the code they touch or annotate. This gives any repo a non-empty trusted layer on day one, with no new workflow to adopt. List them with whyfile list-intent --provenance attested. The capture subcommand records a decision at the moment of choosing, the cheapest time to capture ground truth:
It writes a # Decision: record to docs/decisions/ (idempotent by slug) and merges a provenance: captured node into the graph, immediately queryable via why/explain/list-intent --provenance captured. The node is built through the same ingest path a committed record takes, so re-ingesting the record reproduces the identical node. If --recommendation differs from --chosen, the node records a resolution_delta capturing where the human overrode the agent. --since last-run needs at least two pipeline runs: the first run has no prior snapshot to diff against, so it writes none and digest --since last-run exits 2 with a clear message until a second run has happened.

Drift and affirmation

Recorded decisions rot silently when the code they explain changes underneath them. affirm stamps a decision as reviewed against the current code; drift flags any affirmed decision whose anchored code has changed since, so staleness is visible instead of silent.
affirm records a content fingerprint of the anchored code (symbol-level where the anchor names a function or class, file-level otherwise) into intent-affirmations.json, a small human-owned ledger you commit. drift recomputes those fingerprints and reports four buckets: needs-reaffirmation (affirmed, but the code moved), expired-assumptions (see below), never-affirmed (anchored, staleness unknown), and orphaned (a ledger entry for a decision no longer in the graph). It is deterministic, LLM-free, and informational (exit 0); re-affirming a flagged decision clears it. Assumptions are the other decay mechanism: most stale intent is stale because an assumption quietly expired. A record’s ## Assumptions section (see docs/adr/TEMPLATE.md) is ingested into authored assumption intent nodes, each with an optional review-by date:
An assumption past its review-by date surfaces in drift as an expired assumption, prompting you to revisit the decision that rests on it. --as-of defaults to today; pass a future date to see what will be overdue by then. Because assumptions are first-class intent nodes, explain <file> and why "..." also answer “which assumption governs this path?” during incident triage.

Trust lifecycle

Trust is a lifecycle, not a label. trust resolves each intent node’s current state: it rises through provisional (reconstructed, unverified), corroborated, confirmed (survived an audit), and affirmed (a human signed off), and falls to stale (affirmed but the code drifted since), disputed, superseded (a newer record replaced it), or orphaned (its anchored code was deleted).
States are derived deterministically from the graph plus the affirmation ledger plus a recorded verdict log (intent-trust.json), so they are stable across re-runs. Structural facts (superseded, orphaned) win over promotions, and a human affirm outranks a panel verdict. Recording a verdict appends to the log with the actor and timestamp; it never edits the graph. Superseded and orphaned states are detected automatically from the graph; confirmed and disputed come from recorded verdicts — by hand via --set, or auto-recorded by whyfile audit --record (see above).

Roadmap

Cached Pass B anchoring (to make per-PR updates truly diff-proportional) and --since pipeline scoping are planned.

Feeding intent to external reviewers: review-context

whyfile review-context [--base <ref>] [--files ...] [--format json|markdown|focus] emits a diff-scoped intent brief: for the changed files, the recorded decisions and constraints that govern them, each tagged with its provenance tier (authored/reconstructed) and resolved trust state, plus the constraints with no superseding record in the changeset. It is LLM-free — a projection over intent-diff joined with trust state. It exists so an external code reviewer (e.g. Codex, which auto-reads AGENTS.md) can weight its findings by recorded intent: a diff that violates a blocking constraint (kind=constraint, trust authored/affirmed/corroborated/confirmed) with no superseding decision record is a blocking finding; reconstructed or disputed intent is advisory. --format focus emits a compact, budgeted paragraph for injection as reviewer focus text; markdown (default) is the full block; json is the structured object. This is not a gate — it exits 0/2 only, never EXIT_POLICY.