How I built an interface to query 15 CFR 746.2 with Claude Sonnet 5

Words
1455
Reading
7 min
Listen
Play
5h

alt

The latest solution I tried to improve the readability —and, potentially, the understanding— of the sanctions regime the United States applies to Cuba is a web application that solves a concrete problem of regulatory accessibility: §746.2 of Title 15 of the Code of Federal Regulations—which contains the specific policy toward the Island under the Export Administration Regulations, EAR—lists in paragraph (a)(1) fourteen license exceptions, each one pointing into 15 CFR 740. But in a rare dynamic for well-designed platforms like the Electronic Code of Federal Regulations, eCFR, the hyperlinks are missing here. Reading §746.2 without being able to open those references is reading a list of pointers with no content. You need to open one tab for each license exception you want to analyze. The web application resolves those pointers against the eCFR API and gives access to its annual PDF editions through GovInfo's link service.

I relied on the Claude Sonnet 5 model to generate the full codebase, set at a "low" effort level—and I stress low because nothing about Claude is low or minor—with the thinking function enabled. No need to hide that we use LLMs in the beautiful task of writing code. We should anchor ourselves firmly in that ethic first. The main risk I currently see—for the programmer—is that AI atrophies us, that it makes us lose our engineering instincts. But I also see a natural safeguard: that organic theoretical-practical foundation in us will always define the quality of the output. Whoever doesn't know the business they want to computerize inside out, along with the essences and tendencies of the computerization process itself, won't land on the exact prompt, and won't be able to validate whether the solution they get is the most efficient one either.

The starting point

Since I already had a working tracker for 31 CFR 515, which contains the parent sanctions regime enforced by Treasury's OFAC, I attached that source code to a new chat with Claude so it could evaluate it and, on that basis, design a new application for §746.2. A lot of ground had already been covered there—things like how to work with GovInfo's link handler or how to render the content of a given CFR section. Claude delivered three pieces: an Express backend that queries the eCFR API and caches the XML for each requested section, a hand-built map of the fourteen license exceptions with their exact citation in 15 CFR 740, and a frontend with a section selector that opens the referenced provision's text in a modal window. The design decisions were sound.

Now, Claude has a structural limitation that explains much of what came next. The environment where it runs and tests its own code has no network access to eCFR.gov or GovInfo.gov—it can only reach package repositories and a few code hosts—so upfront it flagged that the entire first version had been built by analogy with behavior already observed in the 31 CFR 515 tracker, with no direct verification against the real source. It was a reasoned delivery, but honest about the chances of some error slipping through.

alt

First fix: the parser didn't survive deployment

When I deployed the application on my laptop with MX Linux, every section—from the root §746.2 down to the license exceptions it references—failed with the same message: "couldn't extract the section from the XML." Claude designed the frontend to look for the text inside a <SECTION> tag, while sections of 15 CFR 740 arrive as a <DIV8 TYPE="SECTION"> element with no inner <SECTION>. This is exactly the kind of error that only surfaces once you deploy against real data.

The LLM's fix added three layers of resilience: a direct lookup by <SECTION>, then a fallback to <DIV8> keyed by its section-identifying attribute, and, as a last resort, a plain-text dump rather than leaving the modal empty. It also switched from walking only the node's direct children to a recursive walk, because when the actual content hangs one level deeper than expected, stopping at the immediate children left the result empty even when the right node was found.

// 1) Direct path: § 740.21...
let section = sections.find(s => {
    const sectno = s.getElementsByTagName('SECTNO')[0];
    return sectno && sectno.textContent.includes(sectionId);
});
if (section) return section;

// 2) Fallback: Part 740's sections arrive as 
// with no inner  at all.
const div8s = Array.from(doc.getElementsByTagName('DIV8'));
section = div8s.find(el => {
    const n = el.getAttribute('N') || '';
    return n === sectionId || n.includes(sectionId);
});

Second round of fixes

Then I ran into other problems. The first was an undocumented quirk with no stated explanation: for GovInfo's link service to assemble the full PDF of §746.2, you have to point it at section number 3, not 2. There's no way to infer this from any documentation; you can only discover it by testing the resulting link in the browser. The fix was to introduce a small table of one-off exceptions in the backend.

// Deployment-observed quirk: GovInfo's link service only assembles
// the full PDF for 746.2 if pointed at sectionnum=746.3.
const GOVINFO_SECTNUM_OVERRIDES = {
    '746.2': '746.3',
};

The second was more substantive. The application was showing, for each exception, the full text of the referenced 15 CFR 740. But §746.2 rarely refers to whole sections there—it refers to specific subparagraphs. The TMP exception, for instance, doesn't authorize all of §740.9, only its (a)(9) subparagraph. Another example: the TSU license exception only applies to Cuba under the conditions described in subparagraphs (a), (b), or (c) of §740.13. Showing the full section in those cases could suggest that everything on screen was authorized for Cuba when only a fragment was. I flagged case by case, for Claude, which exceptions pointed to a single, contiguous subparagraph—and could therefore be isolated without losing context—and which didn't. The edge case was the AVS license exception, which refers to subparagraphs (a), (b), (d), and (e) of §740.15, skipping (c) and leaving out (f): here the solution I proposed was to show the full section but visually highlight the specific subparagraphs that apply to Cuba. I also caught that the exception with no acronym under item (xi) did in fact have a name—Additional Permissive Reexports, APR—and that it pointed specifically to §740.16(h), a detail the first map had left incomplete.

alt

The technical solution Claude implemented to isolate a single subparagraph starts from a fact about the flat XML that the eCFR API returns: nothing is nested. The hierarchy lives only in the marker that opens each paragraph, and one paragraph can open several levels at once. A first version, which read one marker per paragraph and used the character's shape as a tiebreaker, highlighted only 28 of the 68 paragraphs it should have when marking AVS in §740.15. The current one tokenizes every marker and resolves the whole sequence at once. Each marker either continues an open level or opens a child at its first value. Where two readings fit, an (i) after (h) may be the next letter or the first Roman numeral, and what follows decides, with backtracking. If nothing fits, the app says so instead of guessing.

// Simplified from the tracker. A marker either continues an open level or opens
// a child at its first value (candidates() returns the readings that fit).
// Ambiguous markers are settled by the rest of the sequence, not by the
// character's shape.
function go(i, stack) {
    if (i === tokens.length) return true;
    for (const c of candidates(tokens[i], stack)) {
        const next = stack.slice(0, c.depth - 1).concat([{ raw: tokens[i], val: c.val }]);
        if (go(i + 1, next)) return true;   // back up if the rest doesn't fit
    }
    return false;
}

Isolating a fragment doesn't mean showing it alone: (b)(1)(iv) of §740.9 only makes sense next to the "(b)(1)" it modifies. So the tracker shows the fragment with its parent paragraphs dimmed, names the sibling subparagraphs it leaves out, and includes the notes that apply to the parent.

alt

The third problem was the year selector for annual PDF editions. On one hand, it showed an obvious duplication: "most recent edition" and the current calendar year resolved to the same document. On the other hand—and this was the more important observation—the application offered any year from 1997 onward—the base year of GovInfo's available annual-edition history—as an option for every section alike, when each provision has its own year of origin. I gave the example of §740.21, the SCP exception, which came into effect in 2015. The fix adopted the criterion I proposed: take the first date that appears in each section's legislative citation—the Federal Register reference documenting its origin—and use the following year as the selector's floor, since Title 15 updates every January 1st, and so the first annual edition reflecting a change published in a given year is the one for the year after.

// Title 15 updates every Jan 1 — the first annual edition reflecting
// a change published in year X is the one for year X+1.
function floorYearFromCita(citaText) {
    const matches = citaText.match(/\b(19|20)\d{2}\b/g);
    const originYear = parseInt(matches[0], 10);
    return originYear + 1;
}

Some closing thoughts

I don't think a language model could have anticipated any of these three second-round fixes without human intervention. Claude can't read the regulation with the same attention I bring to it when I use it for my own research, nor can it verify GovInfo's actual behavior live from the environment where it runs its own code. What it can do, and did competently, is translate every correction I pointed out into a generalizable technical solution and put it to the test—as far as it's able to test—before handing it back. The subparagraph parser is the clearest example: written without access to the real XML, it only revealed its flaws—and got rewritten—once the real files were put in front of it. The result, then, isn't the work of one well-crafted instruction, but of a fertile sequence of steps in which each actor—the human and the LLM—had to perform at their best to reach a result with any real substance.

Source for the cover image, generated with GPT technology. The screenshots are original ones taken by me.

alt

How I built an interface to query 15 CFR 746.2 with Claude Sonnet 5 | Ecency