Whyfile
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) orreconstructed(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 --gatefails 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 mainsurfaces the intent a PR touches, so a reviewer sees the constraints a change is bound by;whyfile review-context --base mainturns that into a brief an external reviewer like Codex can act on directly. - Retain.
whyfile coverageshows which code has recorded intent and which is dark, andwhyfile digestreports 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
- Installation — requirements, install paths, LLM backend setup.
- CLI reference — every flag, exit code, and output file.
- Caching, resume, and cost — what a run costs, on either backend.
- Querying the intent layer —
explain,list-intent,why. - Gates — the audit panel's convergence gate,
intent-diff, andcheck. - Team collaboration —
changed,coverage,digest,drift,affirm,trust, andreview-context. - Configuring whyfile — the
whyfile.config.jsonartifact. - MCP server — expose the query layer to IDEs and coding agents.
- How it works — the extraction pipeline's Pass A-D mechanics.
- Architecture decisions — every ADR behind this tool, in the order they were made.
- Decision records — the everyday,
captured-tier decisions this repo makes about itself, including this rename.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
855dae9185cfb1046d0ee56601159ca559f2f56c5f9218b5568cb3dfcf82b3bb
|
|
| MD5 |
fe1887cbf401818e9907babe0d359605
|
|
| BLAKE2b-256 |
35c67289ce83b342e487468d86e4d1eb7c396ec0fbb0b7a375a32ad55788b36d
|
Provenance
The following attestation bundles were made for whyfile-0.8.3.tar.gz:
Publisher:
publish.yml on whyfile/whyfile
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
whyfile-0.8.3.tar.gz -
Subject digest:
855dae9185cfb1046d0ee56601159ca559f2f56c5f9218b5568cb3dfcf82b3bb - Sigstore transparency entry: 2210352161
- Sigstore integration time:
-
Permalink:
whyfile/whyfile@dea64736e925203d0334a0edc2ec7d2a6eabb326 -
Branch / Tag:
refs/tags/v0.8.3 - Owner: https://github.com/whyfile
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@dea64736e925203d0334a0edc2ec7d2a6eabb326 -
Trigger Event:
release
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ce3d63223939ea4bd4d3b6a926876be1f8bd1f6a2a84915e1586afb940fb1bcd
|
|
| MD5 |
8034e5af0713cd6ccc03f0b711931876
|
|
| BLAKE2b-256 |
d0d6e205ebb6d14402d0a518db1b3a27fc7dbd9c9df0ac20a778a93099bc5605
|
Provenance
The following attestation bundles were made for whyfile-0.8.3-py3-none-any.whl:
Publisher:
publish.yml on whyfile/whyfile
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
whyfile-0.8.3-py3-none-any.whl -
Subject digest:
ce3d63223939ea4bd4d3b6a926876be1f8bd1f6a2a84915e1586afb940fb1bcd - Sigstore transparency entry: 2210352164
- Sigstore integration time:
-
Permalink:
whyfile/whyfile@dea64736e925203d0334a0edc2ec7d2a6eabb326 -
Branch / Tag:
refs/tags/v0.8.3 - Owner: https://github.com/whyfile
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@dea64736e925203d0334a0edc2ec7d2a6eabb326 -
Trigger Event:
release
-
Statement type: