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.

Metadata

Release files for whyfile 0.8.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for whyfile 0.8.3
File Size Uploaded
whyfile-0.8.3.tar.gz 195.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for whyfile 0.8.3
File Interpreter ABI Platform
whyfile-0.8.3-py3-none-any.whl Python 3 none any Details

Total release size: 379.5 kB

Release files / whyfile-0.8.3.tar.gz

Download URL whyfile-0.8.3.tar.gz
Size 195.9 kB
Tags Source
SHA-256 checksum
How to use checksums
855dae9185cfb1046d0ee56601159ca559f2f56c5f9218b5568cb3dfcf82b3bb
BLAKE2b-256 checksum
How to use checksums
35c67289ce83b342e487468d86e4d1eb7c396ec0fbb0b7a375a32ad55788b36d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 21, 2026.

Transparency log

Release files / whyfile-0.8.3-py3-none-any.whl

Download URL whyfile-0.8.3-py3-none-any.whl
Size 183.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ce3d63223939ea4bd4d3b6a926876be1f8bd1f6a2a84915e1586afb940fb1bcd
BLAKE2b-256 checksum
How to use checksums
d0d6e205ebb6d14402d0a518db1b3a27fc7dbd9c9df0ac20a778a93099bc5605
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 21, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.8.3 This release

2 release files

0.8.2

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.1

2 release 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