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
# Uses your Claude Pro/Max subscription by default (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. A full run over
this project's own docs/adr/ trail grows 172 code concepts into 234 nodes and pulls
62 intent units (18 decisions, 18 mechanisms, 16 constraints, 10 trade-offs) out of the prose,
each grounded to the exact span it came from (100% grounding), 53 anchored back to the code
they explain.
Then the why bears load. Open a PR that edits cache.py — code governed by four constraints
recorded in ADR-0015 — without accounting for them, and the gate
stops the merge:
$ whyfile intent-diff --files src/whyfile/cache.py --gate
Intent diff: introduces 0, supersedes 0, governed 1, conformance-review 1
! src/whyfile/cache.py governed by constraint: Restores extraction, not re-sync, …
Conformance gate: action required — conform to each constraint, or supersede its decision record:
✗ src/whyfile/cache.py — Restores extraction, not re-sync (ADR-0015) → conform, or supersede ADR-0015
✗ src/whyfile/cache.py — Consistent --docs spelling required (ADR-0015) → conform, or supersede ADR-0015
✗ src/whyfile/cache.py — End-of-run sidecar write loses work (ADR-0015) → conform, or supersede ADR-0015
✗ src/whyfile/cache.py — Corrupt entry treated as miss (ADR-0015) → conform, or supersede ADR-0015
$ echo $?
5
That exit 5 is the product. The change conforms to each recorded constraint, or the same PR supersedes ADR-0015 out loud — silently breaking a decision the code is built on is not a path the merge leaves open.
And it stays honest about what it knows. Ask why the code looks the way it does, and every
answer wears its provenance — reconstructed (a model inferred it) never dressed up as authored
(a human recorded it):
$ whyfile explain cache.py --format text
cache.py (code)
• Orphan cache entries accumulate [reconstructed tradeoff] — Orphan cache entries accumulate
over a corpus's lifetime with no automatic pruning.
why: Deferring cleanup accepts unbounded cache growth to avoid deleting live entries, because
auto-prune cannot safely distinguish orphaned entries from ones excluded by the current run.
alternatives: Auto-prune or an immediate --prune-cache flag, deferred because --docs subsetting
makes automatic pruning unsafe.
That rationale isn't a comment in cache.py — it lives in
ADR-0015, which the tool read, distilled, tagged reconstructed,
and wired back to the file the decision governs. Surfacing that round trip is table stakes.
Gating on it — 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 MIT License, Copyright (c) 2026 Will Neill.
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 LICENSE 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.4.0.tar.gz.
File metadata
- Download URL: whyfile-0.4.0.tar.gz
- Upload date:
- Size: 301.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d5871f96243f74ab7d0b7a986b424422c99b03942ae6f80099845ab419572499
|
|
| MD5 |
f71d72caa8d42285c479a5dcb320a3e0
|
|
| BLAKE2b-256 |
74e70cd437a26aad3c473631127d4fa9b743c5614f77c3493bbdf4afdc9d87b5
|
Provenance
The following attestation bundles were made for whyfile-0.4.0.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.4.0.tar.gz -
Subject digest:
d5871f96243f74ab7d0b7a986b424422c99b03942ae6f80099845ab419572499 - Sigstore transparency entry: 2171563378
- Sigstore integration time:
-
Permalink:
whyfile/whyfile@3e6125b11c5f95c04c16ad82013b73a7e8080b62 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/whyfile
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3e6125b11c5f95c04c16ad82013b73a7e8080b62 -
Trigger Event:
release
-
Statement type:
File details
Details for the file whyfile-0.4.0-py3-none-any.whl.
File metadata
- Download URL: whyfile-0.4.0-py3-none-any.whl
- Upload date:
- Size: 115.0 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 |
2f2f6241b023cc7eb89fab114f75ee29ebda5bfdcbcd869e59ca6cf66447003f
|
|
| MD5 |
953dd867cc20b4fc9319b1afbc423b5e
|
|
| BLAKE2b-256 |
425751b99d3eeeef932c348586f933f04c99db3ac23f97d76914f0389c21ea68
|
Provenance
The following attestation bundles were made for whyfile-0.4.0-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.4.0-py3-none-any.whl -
Subject digest:
2f2f6241b023cc7eb89fab114f75ee29ebda5bfdcbcd869e59ca6cf66447003f - Sigstore transparency entry: 2171563412
- Sigstore integration time:
-
Permalink:
whyfile/whyfile@3e6125b11c5f95c04c16ad82013b73a7e8080b62 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/whyfile
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3e6125b11c5f95c04c16ad82013b73a7e8080b62 -
Trigger Event:
release
-
Statement type: