Skip to main content

Whyfile

PyPI version Python versions License: Apache-2.0 Ruff

Your codebase remembers what it does. It forgot why.

whyfile reconstructs that why, keeps it honest — every unit tagged authored or reconstructed, so a model's guess never poses as a decision you made — and puts it on a gate that fails the PR quietly breaking it. Every repo has a Makefile; this is the missing Whyfile. (Formerly graphify-intent; see ADR-0024.)

Why "Whyfile"?

Every repo has a Makefile that says how to build, a Dockerfile that says how to run, and a lockfile that says exactly what ships. None of them say why. Whyfile is the missing file: the decisions, constraints, and trade-offs behind your code, extracted into a queryable graph, anchored to the code they govern, and kept honest by provenance tiers that separate what a human recorded from what a model reconstructed.

The name is also the roadmap. Intent-driven development has one invariant: no unexplained change. Every change either conforms to recorded intent, supersedes it explicitly, or records a new decision. The conformance gate (whyfile intent-diff --gate) enforces it: a pull request that silently violates a recorded constraint does not merge. The why goes on file, and the file bears load.

What it does

whyfile reconstructs the why your code lost: the decisions, the dead ends, and the constraints the awkward bits are quietly paying for — the reasoning that never became a comment because nobody writes # WHY: above the thing they decided not to do. Surfacing rationale that's already written down is table stakes; two things make reconstructed intent trustworthy instead of plausible-sounding fiction:

  • It's kept honest by provenance. Every unit is tagged authored (a human recorded it) or reconstructed (a model inferred it), grounded to the exact source span it came from, so a model's guess never outranks the record or poses as a decision you made.
  • It's load-bearing, not decorative. whyfile intent-diff --gate fails a pull request that changes constraint-governed code without conforming to the constraint or explicitly superseding the decision behind it. The why doesn't just sit in a graph you query — it can block your merge.

Under the hood it reads a graphify knowledge graph alongside your prose docs, runs a short pipeline of LLM passes (extract → anchor → cross-doc relate, plus an opt-in concept-resolution pass), and writes a sidecar JSON, an enriched graph.json, and a report. Three LLM-free query commands read it back. Think of it as the month of code archaeology every new hire does on their way in — done once, checked in, and enforced.

flowchart LR
    D["docs/*.md"] --> A
    G["graph.json"] --> B
    A["Pass A<br/>extract intent"] --> B["Pass B<br/>anchor to concepts"]
    B --> C["Pass C<br/>cross-doc intent"]
    B --> R["Pass D<br/>concept resolution<br/>(opt-in)"]
    A --> OUT
    C --> OUT
    R --> OUT
    OUT["outputs:<br/>.whyfile.json<br/>graph.enriched.json<br/>enrichment_report.md"]

Quick start

# Prefers an API key (ANTHROPIC_API_KEY) — exact prompt tokens, visible cost.
# Falls back to your Claude Pro/Max subscription (needs the `claude` CLI).
whyfile \
  --graph graphify-out/graph.json \
  --docs docs/ \
  --passes A,B,C

See it on this repo

We point the tool at itself, because it's the most honest demo we can give you. Everything below reproduces for $0: graphify update . then whyfile sync builds the deterministic, LLM-free tier in seconds — no API key, no reconstruction run. On this repo at v0.8.2 (2026-07-21) that yields its whole intent layer from its own docs/adr/ and docs/decisions/ trail — every unit authored, captured, or attested, anchored back to the code it explains. The counts below grow as the decision trail does; the shape is the point. (A full reconstruction run adds a reconstructed tier on top; it costs money, and nothing on this page depends on it.)

Then the why bears load. ADR-0016 decided the query layer stays lexical — no embeddings, no graph traversal. That constraint is compiled into intent-rules.json. Add import numpy to src/whyfile/query.py and the gate stops the merge:

$ whyfile check --format text
Conformance check: 1 rule(s), 1 file(s) checked.
  ✗ src/whyfile/query.py imports 'numpy': violates ADR-0016 (No mutation, traversal, or embeddings). the read-only query layer stays lexical: no embedding retrieval and no graph-traversal library. Disagree? Supersede the record, do not fight the linter.
$ echo $?
5

That exit 5 is the product. The change conforms to the recorded constraint, or the same PR supersedes ADR-0016 out loud — silently breaking a decision the code is built on is not a path the merge leaves open. Reproduce it yourself: whyfile sync (LLM-free, seconds, $0), make that one-line edit, run the command above.

And it stays honest about what it knows. Every unit wears its provenance, and the tiers are counted separately — a model's guess is never folded into the trusted number:

$ whyfile coverage --format text
Intent coverage
  Files:   3/121 (2%)
  Symbols: 3/2116 (0%)
  By kind: decision 71 · assumption 20
  Golden fraction: 100% (91/91 trusted) by provenance: authored 58 · captured 28 · attested 5
  Intent debt: 97 dark file(s), 62 orphaned intent node(s) (stale: see `drift`; disputes: audit phase)

100% here is not a boast — it is what "no reconstruction has been run" looks like, stated plainly. Run the paid pass and a reconstructed tier appears in that same breakdown, outside the trusted count, never blended into it. The tool would rather report a small honest number than a large blended one. Gating on the why — and never letting a model's guess pose as ground truth — is the product. (The query commands are covered in Querying the intent layer.)

Team collaboration

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

git diff --name-only origin/main...HEAD | whyfile changed --format markdown

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.

Development

git clone <repo>
cd whyfile
pip install -e ".[dev]"
python -m pytest tests/ -v

Tests cover every module: IDs, section splitting with spans, relation/confidence validation, all four passes (including Pass D's candidate resolution and embedding fallback), merge/enriched-graph assembly, the report, backend resolution, and an end-to-end smoke test. The LLM boundary and the graphify-runtime probe are injected/mocked, so the suite makes no network calls and needs no API key or live backend.

Building the plane while we fly it

Structure tools map imports and call graphs: the what and the how-it-connects. What they can't hand you is the reasoning: which decision a file is the consequence of, what got tried and thrown away, why the ugly workaround earns its keep. whyfile is a thin reasoning layer over the structural graph graphify already builds: a decision/strategy view, not another entity extractor, and deliberately not a vector DB or a RAG stack (that would be a much heavier machine than the job needs).

This whole docs/adr/ trail is fair game — decisions, rationale, and the alternatives considered — written by hand as we built it, so the reasoning actually moves:

  • ADR-0002 bets the whole tool on intent, not generic concept extraction.
  • ADR-0004 ships v1 embedding-free and defers cross-document resolution, which ADR-0009 later delivers.
  • ADR-0010 openly supersedes an earlier README claim after a clean install exposed a silently broken package.
  • ADR-0011 catches extraction quietly running on Opus and puts it on a budget.

That recurring beat — a decision that doesn't survive contact with the code — is the whole reason this tool exists.

Explore the docs

License and attribution

whyfile is licensed under the Apache License 2.0, Copyright © 2026 Four Birds Limited (New Zealand), which maintains the project. The name "whyfile" is a trademark of Four Birds Limited — code is free under Apache-2.0; naming and compatibility claims are governed by the trademark policy.

This project is an independent post-processor built to interoperate with graphify by Safi Shamsi. It reuses graphify's graph schema and relation vocabulary and calls graphify as a separately-installed runtime dependency; no graphify source code is bundled with or distributed as part of this project. graphify is licensed under the MIT License (Copyright (c) 2026 Safi Shamsi); see the ACKNOWLEDGEMENT AND ATTRIBUTION section of this repository's NOTICE file and the upstream license for the full text. With thanks to the graphify project.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

whyfile-0.8.3.tar.gz (195.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

whyfile-0.8.3-py3-none-any.whl (183.6 kB view details)

Uploaded Python 3

File details

Details for the file whyfile-0.8.3.tar.gz.

File metadata

  • Download URL: whyfile-0.8.3.tar.gz
  • Upload date:
  • Size: 195.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for whyfile-0.8.3.tar.gz
Algorithm Hash digest
SHA256 855dae9185cfb1046d0ee56601159ca559f2f56c5f9218b5568cb3dfcf82b3bb
MD5 fe1887cbf401818e9907babe0d359605
BLAKE2b-256 35c67289ce83b342e487468d86e4d1eb7c396ec0fbb0b7a375a32ad55788b36d

See more details on using hashes here.

Provenance

The following attestation bundles were made for whyfile-0.8.3.tar.gz:

Publisher: publish.yml on whyfile/whyfile

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file whyfile-0.8.3-py3-none-any.whl.

File metadata

  • Download URL: whyfile-0.8.3-py3-none-any.whl
  • Upload date:
  • Size: 183.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for whyfile-0.8.3-py3-none-any.whl
Algorithm Hash digest
SHA256 ce3d63223939ea4bd4d3b6a926876be1f8bd1f6a2a84915e1586afb940fb1bcd
MD5 8034e5af0713cd6ccc03f0b711931876
BLAKE2b-256 d0d6e205ebb6d14402d0a518db1b3a27fc7dbd9c9df0ac20a778a93099bc5605

See more details on using hashes here.

Provenance

The following attestation bundles were made for whyfile-0.8.3-py3-none-any.whl:

Publisher: publish.yml on whyfile/whyfile

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.8.3 This release

2 files

0.8.2

2 files

0.8.1

2 files

0.8.0

2 files

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

0.4.0

2 files

0.3.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page