The business caseThe problem this solves
An operating permit is a numbered list of conditions and a site has to be able to say, for each one, what it owes and when. That is not one question, it is five: does this condition put anything on a calendar at all, how often, what is the date anchored to, where does the submittal go, and what is the next date. Most of it is not judgement -- "within 30 days after the end of each calendar quarter", read against a stated as-of date, resolves to exactly one date and a hundred lines of integer arithmetic settle it. THE HARD PART IS THAT THE CONDITION IS NOT ALWAYS WHERE THE ANSWER IS. A frequency printed in the condition can have been changed by an amendment three sections down. A destination can have been redirected. A condition can have been stayed by a letter, in which case the right answer is NO DATE and a date is a false alarm. And a condition can recite a complete, parseable schedule and then say, in its last clause, that it establishes no obligation -- which every keyword extractor puts on the calendar and nobody ever takes off. A coordinator reading a permit condition by condition with a spreadsheet open: deciding whether each numbered clause is an obligation at all, reading its frequency and its day count out of the sentence, working the next due date against the reporting calendar, checking the submittal log for what is already in, and then reading the amendment history and every agency letter to find which of those answers has since changed.
Audience
Site environmental coordinators, EHS managers and compliance leads at facilities holding air, water, waste, injection or extraction permits -- and anyone who has to hand a reporting calendar to the people who actually file, and then defend a missed date. Every number on these pages came from one real run of this code, not from a vendor page.
The inputThe actual permit compliance packs
The corpus is 40 permit compliance packs, 0.21 MB (txt 40). ⚠︎ THIS CORPUS IS INVENTED -- generated from a fixed seed, not collected. Three reasons, and only the third is about difficulty. First, HALF THE DOCUMENT DOES NOT EXIST IN PUBLIC: the permit is usually published, the site's compliance posture never is, and the posture is where the hard cases live. Second, the interesting defect hides in the scrubbing -- an amendment is findable only because it names a condition number, and any scrubber careful enough to be safe changes the answer. Third, THE HARD CASES HAVE TO BE PLACED: this kit's whole claim is a comparison on cells where free code and a model must differ, and in a collected corpus you get whatever proportion the sample happened to contain and find out after you have paid for the run. Here the denominators are decisions, stated in corpus-stats.json and asserted by evals/check_labels.py.
The corpus
- The 40 permit compliance packsgenerated from a fixed seed, so no real record, person or institution appears in it.
- Where each came fromwritten for this kit rather than collected — the corpus is generated in the kit's own repository, so there is no third-party data in it.
Swap this folder for your own material and the kit is pointed at your permit compliance packs. That is the whole change — there is no database to migrate.
Permit Compliance Extract
----------------------------------------------------------------
Pack PRM-0001
Operator Calderfield Resources LLC
Facility Dunmoor Crude Terminal
Facility reference FAC-4000
Permit number AQ-2024-0100
Permit type Air Quality Operating Permit
Issued 2024-08-20
Effective 2024-10-19
Expires 2029-10-19
As-of date 2026-08-26
Conditions listed 8
Reporting Destinations
----------------------------------------------------------------
RD-1 Division of Environmental Quality, Compliance Section -- electronic submittal portal
RD-2 Regional Field Office, District 1 -- paper or electronic submittal
RD-3 Retained on site; no submittal unless requested in writing by the Department
RD-4 Office of Water Resources, Permits and Reporting Branch -- electronic portal
Permit Conditions
----------------------------------------------------------------
Condition 4.1
Text The Permittee shall at all times maintain the 3-A vapour recovery unit in
continuous operation whenever the tanks are in service. Records
demonstrating compliance shall be retained on site and made available to
the Department upon request.
Condition 4.2
Text The Permittee shall submit a continuous emission monitoring system
availability report to the Division of Environmental Quality, Compliance
Section no later than the sixtieth day following the close of each
calendar quarter.
Condition 4.3
Text The Permittee shall complete the initial fugitive emissions leak survey of
all regulated components and submit the results to the Regional FieldAbridged — the file continues.
The outcomeWhat a good result looks like
A drafted compliance calendar: one row per permit condition, in the permit's order, each carrying a disposition, a frequency, a due basis, a reporting destination and a next due date computed against the pack's own as-of date -- or an honest null where no date exists yet. Plus a pack-level calendar action. Nothing is filed, scheduled or notified.
And when it cannot
⚡ THE PAID ARM WINS THE HEADLINE -- 90.62 pct against the strongest free floor's 81.88 pct over the same 320 condition cells (r001-permit-condition against b002-permit-condition-scheduleparse), 8.74 points -- AND THE HEADLINE IS THE LEAST INTERESTING NUMBER HERE, because the two arms fail in completely different places. ⚡ THE PAID ARM PUT ZERO FALSE DEADLINES ON THE CALENDAR: 0 of 40 conditions that impose no obligation were given a date, against 14 of 40 for the free floor, and 0 of 132 cells that should carry no date got one, against 26 of 132. It also took every prose-settled channel outright -- 16 of 16 amended frequencies, 16 of 16 redirected destinations, 12 of 12 stayed conditions, and 44 of 44 basis references cited correctly with nothing invented -- where every free floor scores 0.00 pct on all four, because the answer is not in the condition's sentence. ⚠︎ AND ITS 30 MISSES ARE THREE THINGS, NONE OF THEM READING. TWELVE are one vocabulary boundary: a condition due "on or before 1 January and 1 July of each year" was called FIXED_DATE where the key says ANNIVERSARY -- and ALL TWELVE GOT THE DATE EXACTLY RIGHT, so the thing a calendar is actually built from was correct on every one. EIGHT are the sentences written to defeat a keyword gate: it declined to put a date on any of them, which is the expensive half, and then could not name what kind of non-obligation it was. TEN are date arithmetic, and every single one is off by a WHOLE NUMBER OF PERIODS -- +30, +92, +181, +184, +365 days, never by a day or a week -- with seven of the ten on conditions whose frequency it had just correctly read out of an amendment. ⚠︎ SO THE HONEST SUMMARY IS THAT THE MODEL BOUGHT THE READING AND LOST SOME OF THE ARITHMETIC THE FLOOR IS HANDED FOR FREE.
Where it fitsWhat did work
Every line below is a measured result from this kit's own runs, with the figure that supports it. The headline above is not softened by any of them.
- Your permits come from one agency, on one template, and nothing outside the condition ever changes what a condition means -- no amendments, no stays, no interpretive clauses. — the free floor -- schedule-parse, $0.00
It reaches 81.88 pct on the discriminator, 92.02 pct on the dates it does recover, and runs all 40 packs in well under a second with no key. On the 262 cells whose answer IS in the sentence it is at or near the paid arm everywhere. - Amendments, stays and interpretive conditions are ordinary in your permits -- which is to say, you have permits. — the fast tier, $0.050729 a permit
It took 16 of 16 amended frequencies, 16 of 16 redirected destinations and 12 of 12 stayed conditions, cited the amendment or letter correctly on 44 of 44, invented nothing, and put ZERO false deadlines on the calendar against the floor's 14 of 40. - You want the best of both and are willing to write fifty lines. — NOT MEASURED HERE, and it is the first thing to build
Every date error the paid arm made is arithmetic, and every arithmetic error the floor made is reading. A design where the model returns the SCHEDULE -- frequency, basis, offset, anchor, and whether an amendment or a letter changed it -- and src/dates.py computes the DATE would take the model's reading and the code's arithmetic. This kit did not build it and does not claim it would win.
And where nothing here is good enough:
- Anyone who can drop a PDF into the site's document folder is outside your trust boundary. — neither arm, unchanged
One ordinary-looking agency letter appended to the correspondence emptied the calendar on 20 of 20 packs and withdrew 82 of 141 correct deadlines (58.16 pct). Nothing in this kit defends against it and no defence is claimed.
At a glanceHow the whole thing runs
Run once, for real, on 2026-08-26. Every figure on these pages was captured from that run — nothing is written from intent.
14 steps, grouped by the question that sends you to them rather than by build order. Each tile carries the one figure that step is about, and opens the page behind it.
Should you use this?What you bring, where it stops, and when not to use it
Before you commit an afternoon to this, these are the answers that decide it. Each one is rendered from the record it lives in — and links the page that holds it in full.
| What do I have to bring? | ⚡ REPRODUCIBILITY FIRST, VERIFIED RATHER THAN ASSERTED: tools/build_corpus.py was re-run over the shipped tree and the 40 packs, the answer key and the statistics file came back BYTE-IDENTICAL -- one SHA-256 over corpus/*.txt + gold.jsonl + corpus-stats.json, equal either side. ⚠︎ src/select.py WITHHOLDS A NAMED SECTION AND IS NOT A REDACTION SYSTEM. Corpus lens → |
| When is this the wrong choice? | Avoid: The moment a single amendment exists it is silently wrong and stays wrong, because nothing in its input mentions the amendment. That is the case against the best-fitting scenario (“Your permits come from one agency, on one template, and nothing outside the condition ever changes what a condition means -- no amendments, no stays, no interpretive clauses.”). 4 scenarios scored in all, each with its own. Eval lens → |
| Where does it stop working? | A permit layout that is not this one. src/pack.py is a set of regular expressions written for these headings, this fixed-width submittal table and these key/value blocks; against a real agency's permit PDF it parses nothing and returns empty tables rather than guessing. 8 recorded failure modes, each from a run rather than a guess. Corpus lens → |
| What was never verified? | WHETHER A SECOND RUN WOULD AGREE. Every arm here ran ONCE. 7 items this kit says it could not check. Eval lens → |
| Can I run this on a model I control? | Yes — any OpenAI-compatible endpoint, including one on your own hardware. The shipped adapter takes its host from BASE_URL and its model from MODEL, so nothing in src/ changes. The published figures come from 1 model on the fast tier, one provider, one key. Prompt lens → |
| And if it fits — what do I stand up? | 4 artifacts with a stated home and a stated egress, and 3 decisions each with what you provision past its ceiling — plus what was not measured. That is the next page, not this one. step 14 — Run it in your environment → |
Not asked of this kit — 2 questions: clone (a fresh clone of this kit runs with nothing fetched); judge (nothing here is graded by a model).
Last verified 2026-08-26 — r001-permit-condition. Every figure on these pages was captured from that run.
Run itHow this reaches your data
Every result on this page was produced by pure code over checked-in files, with no API key — which is why you can read the numbers before anyone spends anything.
Run this on your own data
- The pipeline, its eval harness and the runs behind every numberdeployed inside your environment, on your own model endpoints, against your own documents.
- The corpus above is the shape, not the limitit is a folder swap, and there is no database to migrate.
Checked before this shipped — Clone, python3 -m src.app, open the port. No key, no install, no index build: the corpus, the answer key, the schedule grammar, the date arithmetic, all three free floors and every committed result file are already there, and the page renders and stays browsable offline. requirements.txt names nothing.



