Your Agent's Favorite Legal Database

Words To Data is a tool that let's you and your agents manage the The U.S. Code, the laws that change it, and the court opinions that cite it, as one dataset.

What It Is

A dataset of the law, with evidence

words_to_data is a Rust command and library. It builds a SQLite dataset from public sources, and it answers questions about that dataset.

The dataset holds the U.S. Code at each release point that you add. It holds the public laws that amend the Code, and it links each amendment to the change that it made. It holds court opinions from CourtListener, by page and paragraph, and it links each U.S.C. citation to the provision that the court cited. It can also hold House roll calls.

Rules build each link from the publisher's markup. No language model writes a link. When the rules cannot place an amendment, the amendment goes on a work list with the reason.

Who it is for

  • AI agents that must answer legal questions from a record, not from memory. Each answer comes with words to quote and an address to check.
  • The people who check those agents. Each link shows how it was made. A person can confirm or refute it, and the review stays as its own record.
  • Researchers and developers who need the Code, its changes and their causes as data. They can use the command, the SQLite file, or the Rust library.

Try SLEUTH

SLEUTH is the official agent skillset for words-to-data. Install it into pi, or have your own agent install it themselves, then ask a question about the law. You do not need to build a dataset: /sleuth-setup downloads the command and an example dataset.

Three steps

You need 6 GB of free disk space. You do not need Rust, curl or zstd.

  1. Install pi. The installer also installs Node.js when it is necessary.

    curl -fsSL https://pi.dev/install.sh | sh
  2. Install SLEUTH into pi.

    pi install git:github.com/wordstodata/sleuth-pi
  3. Start pi. Type /login to connect a model with a subscription or an API key. /sleuth-setup downloads the words_to_data command for your computer (Linux, macOS or Windows) and the example dataset (280 MB, 5.3 GB on disk), and opens the dataset. Then ask a question:

    $ pi
    /login
    /sleuth-setup
    # Then type your question:
    › Where in the Internal Revenue Code is the new Trump account section, and in which printing of the Code does it first appear?

The session is read-only. SLEUTH finds the section from the words alone, and it shows the evidence.

Then ask more questions, for example:

  • What does 26 U.S.C. § 280C(c)(1) say now, and what did it say before the One Big Beautiful Bill Act changed it?
  • In Boeing Co. v. United States, what did Justice Thomas say at 537 U.S. 459, and which Code sections does his dissent cite?
  • BMW (US) Holding Corp. v. United States (Court of Federal Claims, August 2026) cites the bonus depreciation rule, 26 U.S.C. § 168(k). Has § 168(k) changed since the decision?

Features

📚

The U.S. Code by Release Point

Each release point of each title is one expression, such as uscode/title_26@2025-07-30. diff and path show what changed between two release points, field by field.

🔗

Amendments Linked to Changes

Public laws come from Congress.gov. A rule places each amendment from its address in the bill's markup, the first window after enactment, and the words it quotes. The OLRC's classification tables add a second source.

⚖️

Case Law Linked to the Code

add-opinions adds cases from CourtListener. Each case keeps its writings (the opinion of the court, concurrences, dissents) with their type and author, and each writing keeps its pages, paragraphs and footnotes, so a pin cite such as 537 U.S. at 459 is a path. Each U.S.C. citation becomes a link from the paragraph that holds it to the subdivision that the court wrote. cases-citing tells you if a cited provision changed after the court filed the opinion.

🧾

Evidence for Every Link

settle --explain shows the source, the method and its version, the address, the window, and why the rule chose that change. An agent can quote it.

✅

Review and Correction

settle records a verdict as its own record. link-amendment records a link that an agent found, and refuses a path that did not change. Nothing is deleted. The newest review stands.

🧩

A Work List, Not a Guess

residue lists each amendment that no link names, and the stage and reason where the rule stopped. coverage shows the changes that no amendment explains.

🗳️

House Votes

votes shows how the House voted on a bill, counted by party on the date of the vote. The source has no Senate votes, so the dataset holds House roll calls only.

🔍

Answers That Fit in Context

search gives scoped snippets. Lists stop at 20 rows and tell you how many rows they did not show. --work, --at, --path and --bill narrow the output, and --json gives rows for a program.

🚧

It Says What It Does Not Know

When the dataset holds no release point for a period, the commands say "out of scope". They do not say "unchanged". declare sets what a dataset is meant to cover, so that "not found" and "out of scope" are different answers.

🌱

Datasets That Grow

add-release-points and add-bills add data to a dataset that you have. A grown dataset gives the same links as a new build. All downloads stay in a cache, and --offline reads only the cache.

🦀

A Rust Library

The crate words-to-data opens a dataset and reads it from your own code. With default-features = false, it leaves out the command line and its dependencies.

What Comes Next

The roadmap shows the work in progress and the work that we plan.

View Full Roadmap

Examples

Each output below is real. We ran each command with words-to-data 0.6.0 on a dataset that holds the U.S. Code at 5 release points (2025-07-18 to 2026-07-12), 7 bills of the 119th Congress, and 28 court cases. Long output is cut, and "…" shows each cut.

1. Find a provision from its words

Search one title at one release point. Each result gives its path, its field and a snippet.

words_to_data search law.sqlite "domestic research or experimental" --work uscode/title_26 --at 2026-07-12

Output

…
uscode/title_26@2026-07-12  uscode/title_26/subtitle_A/chapter_1/subchapter_B/part_VI/section_174A  [heading]  1 occurrence
    Domestic research or experimental expenditures
uscode/title_26@2026-07-12  uscode/title_26/subtitle_A/chapter_1/subchapter_B/part_VI/section_174A/subsection_a  [content]  1 occurrence
    Notwithstanding section 263, there shall be allowed as a deduction any domestic research or experimental expenditures which are paid or incurred by the taxpayer…
…
12 fields, 12 occurrences

2. See what changed, and which amendment did it

Give a path and two expressions. The output shows each changed field, the old and new text, and the amendment linked to the change.

words_to_data path law.sqlite uscode/title_26/subtitle_A/chapter_1/subchapter_B/part_IX/section_280C/subsection_c/paragraph_1 \
    --from uscode/title_26@2025-07-18 --to uscode/title_26@2025-07-30 --exact

Output

PATH: uscode/title_26/subtitle_A/chapter_1/subchapter_B/part_IX/section_280C/subsection_c/paragraph_1
Present in: uscode/title_26@2025-07-18 (1 provision), uscode/title_26@2025-07-30 (1 provision), uscode/title_26@2025-08-14 (1 provision), uscode/title_26@2026-01-23 (1 provision), uscode/title_26@2026-07-12 (1 provision)

Provisions (uscode/title_26@2025-07-18 -> uscode/title_26@2025-07-30) (1):
  in both — from position 0, to position 0
      chapeau: "If—" -> ""
      content: "" -> "The domestic research or experimental expenditures (as defined in section 174A(b)) otherwise taken into account as a deduction or charged to capital account under this chapter shall be reduced by the amount of the credit allowed under section 41(a).\n"
      continuation: "the amount chargeable to capital account for the taxable year for such expenses shall be reduced by the amount of such excess." -> ""

Annotations on this path exactly (1):
  [Pending] uscode/title_26 2025-07-18 -> 2025-07-30  amend 119-hr-1 amd 15684ad7c34c (conf -, by rule:evidence_matching, address, window and quoted words@6)
      (B) Section 280C(c)(1) is amended to read as follows:139 STAT. 192"(1) Reduction.In general.-The dom…
      link 7c1690c8ef8f  uscode/title_26/subtitle_A/chapter_1/subchapter_B/part_IX/section_280C/subsection_c/paragraph_1  [quoted_words]

OLRC classifications of uscode/title_26/subtitle_A/chapter_1/subchapter_B/part_IX/section_280C (2):
  classified by the OLRC from Pub. L. 119-21 §70302(b)(2)(B), amended
  classified by the OLRC from Pub. L. 119-21 §70304(b), amended

3. Ask why a link is true

First find the link on a path. Then settle --explain shows how the rule made it. With --explain alone, settle only reads.

words_to_data annotations law.sqlite --bill 119-hr-1 --path uscode/title_26/subtitle_A/chapter_1/subchapter_B/part_VI/section_174A

Output

  [Pending] uscode/title_26 2025-07-18 -> 2025-07-30  insert 119-hr-1 amd 2841e731435d (conf -, by rule:evidence_matching, address, window and quoted words@6)
      (a) In General.-Part VI of subchapter B of chapter 1 is amended by inserting after section 174 the f…
      link 7cdb05c5323a  uscode/title_26/subtitle_A/chapter_1/subchapter_B/part_VI/section_174A  [quoted_words]
1 annotation(s)
words_to_data settle law.sqlite --link 7cdb05c5323a --explain

Output

Link 7cdb05c5323a
  legislature.amended_by said by rule:evidence_matching
  uscode/title_26/subtitle_A/chapter_1/subchapter_B/part_VI/section_174A
  -> legislature.amendment:119-hr-1:2841e731435d7fb5b093e5bd6a39be845b1f565418a78949b0d8e0820ac2e62b

How it was made:
  Source: rule:evidence_matching
  Method: address, window and quoted words@6
  Address: /us/usc/t26/s174A, from the bill's markup
  Window: uscode/title_26@2025-07-18 to 2025-07-30
    the first window after the law's enactment on 2025-07-04 in which something under the
    address changed.
  Changes under the address: 1
  Chosen: by quoted words
    the change shows the words the bill quotes: enacted text 1 of 1.

What the object says:
  (a) In General.-Part VI of subchapter B of chapter 1 is amended by inserting
  after section 174 the following new section:"SEC. 174A. 26 USC 174A.DOMESTIC RESEARCH OR EXPERIMENTAL
  EXPENDITURES."(a) Treatment as Expenses.-Notwithstanding section 263, there shall be allowed as a deduction any
…
The provision is new in the window. It is at uscode/title_26@2025-07-30 uscode/title_26/subtitle_A/chapter_1/subchapter_B/part_VI/section_174A.
  The words added (19 fields):
    section_174A [heading] Domestic research or experimental expenditures
    section_174A/subsection_a [heading] Treatment as expenses
…

4. Find what the rules could not link

residue is the work list for a law. It gives the stage where each amendment stopped, and the reason.

words_to_data residue law.sqlite --bill 119-hr-1

Output

121 amendment(s) that no standing link names
    55  work
             9  address: no level above the clause says what it amends
             2  address: no section under amendment was named
…
             8  resolve: another amendment's words fit a change under its address as well as its own, and nothing breaks the tie
…
             4  resolve: the words it quotes show in none of the 1 change(s) under its address
…
     0  linked by the evidence method, and not written: run link-by-evidence
    38  not held: the amendment changes something the dataset does not hold
    28  quiet: nothing under the address changed after the law's enactment; not work
     0  reviewed: no link: a reviewer concluded the amendment has no correct link; not work

5. Find the cases that cite a section

Give a citation as a court writes it. For each opinion, cases-citing tells you if the provision changed after the court filed it. For a period with no release point in the dataset, it says "out of scope".

words_to_data cases-citing law.sqlite --cites "26 U.S.C. § 174"

Output

26 U.S.C. § 174 names uscode/title_26
  /us/usc/t26/s174 resolves to 1 provision(s)

=== uscode/title_26/subtitle_A/chapter_1/subchapter_B/part_VI/section_174

10 opinion(s) cite it:
…
  United Stationers, Inc. v. United States (judicial/case_1527901/opinion_1527901, filed 1997-10-16)
    opinion text: courtlistener:opinion/1527901:xml_harvard / ocr@1 / MachineSuggested
    cites uscode/title_26/subtitle_A/chapter_1/subchapter_B/part_VI/section_174, from 1 passage(s):
      judicial/case_1527901/opinion_1527901/page_1282/paragraph_2 — link ebed0c1162c7 is MachineSuggested, matched "26 U.S.C. § 174"
      decided 1997-10-16. The link was resolved in the printing of 2025-07-18, the earliest held: this dataset holds no printing of the law as it stood on 1997-10-16, so that law is out of scope.
      wrote the section alone
      2025-07-18 → 2025-07-30: CHANGED at uscode/title_26/subtitle_A/chapter_1/subchapter_B/part_VI/section_174; 4 path(s) changed: uscode/title_26/subtitle_A/chapter_1/subchapter_B/part_VI/section_174/subsection_a, uscode/title_26/subtitle_A/chapter_1/subchapter_B/part_VI/section_174/subsection_a/paragraph_2/subparagraph_B, uscode/title_26/subtitle_A/chapter_1/subchapter_B/part_VI/section_174/subsection_b, and 1 more
      2025-07-30 → 2025-08-14: unchanged at uscode/title_26/subtitle_A/chapter_1/subchapter_B/part_VI/section_174
      2025-08-14 → 2026-01-23: unchanged at uscode/title_26/subtitle_A/chapter_1/subchapter_B/part_VI/section_174
      2026-01-23 → 2026-07-12: unchanged at uscode/title_26/subtitle_A/chapter_1/subchapter_B/part_VI/section_174
      1997-10-16 → 2025-07-18: OUT OF SCOPE. This dataset holds no printing of the cited work in that period, so it cannot say whether the provision changed in it. It is not a statement that nothing changed.
…

6. Read an opinion by page and paragraph

An opinion is stored by page and paragraph, so a pin cite becomes a path. Here, INDOPCO, Inc. v. Commissioner at 503 U.S. 84:

words_to_data search law.sqlite "strictly construed" --work judicial/case_112697

Output

judicial/case_112697@1992-02-26  judicial/case_112697/opinion_112697/page_84/paragraph_1  [content]  1 occurrence
    …ssioner
v.
Idaho Power Co.,
418 U. S., at 16. For these reasons, deductions are strictly construed and allowed only “as there is a clear provision therefor.”
Ne…
1 field, 1 occurrence

7. See how the House voted

votes counts each position by party, as held on the date of the vote.

words_to_data votes law.sqlite 119-hr-1

Output

Bill: 119-hr-1

Roll call 190 on 2025-07-03T14:31:00-04:00 — Passed
  On Motion to Concur in the Senate Amendment
  Democrat     Nay         212
  Republican   Yea         218
  Republican   Nay           2

SLEUTH, the Official Agent

SLEUTH (Structured Legal Examination Using Tracked Histories) is a package for the pi coding agent. It answers legal questions from a words-to-data dataset, and it shows the evidence.

What it adds to pi

  • A tool for each command, such as w2d_search, w2d_path, w2d_cases_citing and w2d_settle.
  • Four skills from the words-to-data playbook: answer a question, resolve the residue, review links, and build a dataset.
  • A write gate. A session is read-only. Only a person can give write access, with /dataset <path> --write. The agent cannot change which dataset is in use.
  • A gap list. When no command answers a question, SLEUTH can use read-only SQL. It records each such case as a gap, and /sleuth-gaps shows the list.

The example dataset

Try SLEUTH on a dataset that we built. It is 280 MB to download and 5.3 GB on disk. It holds:

  • The U.S. Code, every title, at 5 printings: 2025-07-18, 2025-07-30, 2025-08-14, 2026-01-23 and 2026-07-12.
  • 7 bills of the 119th Congress, with their amendments linked to the changes in the Code, and the House roll calls. Two of them are H.R. 1 (Pub. L. 119-21, the One Big Beautiful Bill Act) and S. 1071 (the NDAA for FY2026, Pub. L. 119-60).
  • 49 court writings in 28 cases, with pages, paragraphs, footnotes and their citations of the Code.

How well it works

We test SLEUTH with a benchmark of questions in five sets: bills, case law, the Code, seed questions and traps. A trap asks for something that the dataset cannot answer, or tells the agent to break a rule. The benchmark runs SLEUTH on a local model, and a script grades each answer.

304 / 314runs passed (97%), 2 runs per question, 2026-10-26, words_to_data with the code of 0.6.1, graded again with corrected answer keys. 2 of the 10 failed runs stopped on a model-server error
6 agentswith only the playbook, the binary and a dataset, answered 4 questions about Pub. L. 119-21 correctly (words-to-data PR #267)
55 → 3items in the residue of Pub. L. 119-21 after those agents worked it

Known weak spots: the dataset has no case-to-case links, so SLEUTH cannot answer questions about case history. Some court records hold the words of more than one author, so the agent must read the author from the text.

Any other agent can use the same playbook: give it the words_to_data binary, a dataset, and docs/agents/working-a-dataset.md.

SLEUTH on GitHub

Build Your Own Dataset

The example dataset is enough to try SLEUTH. To work on other bills, other printings of the Code, or other cases, build your own dataset. You need approximately 3.5 GB of disk space and a free Congress.gov API key. You need Rust only for the library, or to build the command from source.

The command

Download the command, then build a dataset. These steps get two release points of the whole Code and Public Law 119-21, and link the law to the changes that it made.

Bash
curl -fsSL https://github.com/wordstodata/words-to-data/releases/latest/download/words_to_data-x86_64-unknown-linux-musl.tar.gz | tar xz
sudo mv words_to_data /usr/local/bin/
export CONGRESS_API_KEY=<your key>

words_to_data build-dataset law.sqlite --uslm-dates 2025-07-18,2025-07-30 --bills 119-hr-1
words_to_data add-classifications law.sqlite
words_to_data link-by-evidence law.sqlite

This line is for Linux on x86_64. The releases page has the command for Linux (x86_64 and aarch64), macOS (Apple silicon and Intel) and Windows (x86_64). If you have Rust, cargo install words-to-data also installs the command.

To add court opinions, set COURTLISTENER_API_KEY and use add-opinions. words_to_data --help lists each command.

The Rust library

Add the crate to Cargo.toml. Then open a dataset and read the annotations on a path.

TOML
[dependencies]
words-to-data = { version = "0.6", default-features = false }
Rust
use words_to_data::dataset::Dataset;

let dataset = Dataset::open_sqlite("law.sqlite")?;
let path = "uscode/title_26/subtitle_A/chapter_1/subchapter_B/part_VI/section_174A";
for annotation in dataset.annotations_for_path(path)? {
    println!("{:?} by {}", annotation.operation, annotation.source_bill.bill_id);
}
// Insert by 119-hr-1

Upgrade from 0.6.0: the stored format does not change, so a 0.6.0 dataset opens with 0.6.1. To get the new links, run redesignations for each bill and window, then link-by-evidence again, and add the opinions again with add-opinions. An input that the dataset does not hold now stops a command with exit 1 and the notice on stderr. The changelog lists the changes that break code and agents.

Documentation & Resources

🔧

GitHub Repository

The source code, the issues, and the changelog. The latest release is v0.6.1 (2026-10-26).

View Source
📦

Crates.io and docs.rs

Get the crate from the Rust package registry. The API documentation is on docs.rs.

View Crate API Docs
🤖

The Agent Playbook

How an agent answers a question with evidence, resolves the residue, and reviews links, with the command alone.

Read the Playbook
📖

Concepts and Decisions

CONTEXT.md is the glossary: work, expression, path, link, review. The ADRs record each design decision and its reason.

How a Change Is Linked

Get in Touch

Send us your questions, feedback, or partnership requests.

📧
contact@wordstodata.com

For technical support, open an issue on GitHub.