The mining file
Reads technical reports so I can compare junior miners on what is proven rather than what is claimed — and refuses every number it cannot trace back to a page.
982 commits · first 23 Jul 2026 · last 7 Oct 2026 · 76 days
commits per week, counted from the repository when this page was last built

Thirty-nine companies, and no two of them counting the same way
I hold a basket of junior mining companies. Each publishes a technical report — a few hundred pages of PDF written to a Canadian standard — and each report carries the numbers that decide whether a company is cheap or merely small: how much metal is in the ground, at what grade, how confidently it was measured, what it would cost to get out.
I expected the hard part to be reading those reports. It was not. The hard part is that two numbers which look identical on the page are frequently not the same number. A grade before dilution and a grade after it. An ounce inside a resource and an ounce inside a reserve. A dollar that is Canadian and a dollar that is not. Everything below is the story of forcing words to mean one thing — and it is far more of the work than the reading ever was.
First commit
6ad5a76 23 Jul 2026
It did not begin as a file on companies at all. The first commit is a weekly macro tracker: four checkable triggers, thresholds written in code, a snapshot appended to a history file, and a message to my phone only when a state actually changes. No language model anywhere in it.
That is the ancestor of everything that followed, and it explains how the later thing behaves. From day one a number came out of a computation with a threshold I could read, not out of an opinion.
A fact book instead of a table with columns
501d7c9 15 Aug 2026
The obvious shape for this is a table: one row per company, one column per figure. It dies at the first new figure. The database I started from carried columns for gold, silver and copper; when zinc and lead turned up, columns were appended and never filled, and manganese never got one at all. At one deposit zinc and lead could not be valued although they are 27 % of the metal there.
So a row stopped being a company and became a single statement about a company. A new figure is a new row plus one entry in a list of known quantities, not a rebuild. Two dates instead of one, as well: the day the statement is valid for, and the day I wrote it down. Without that split a resource from March 2011 looked exactly as fresh as one from June 2020, and both as fresh as one from this year.
And the rule that has held ever since: no silent conversion. A fact that arrives carrying the wrong unit is refused, not quietly fixed. The first run refused three of them.

“$M” does not say which dollar
4c6c795 17 Aug 2026
Economic tables often carry their own unit column, and then the unit belongs to the row rather than the column header — above the value column nothing can stand, because dollars, years and percentages sit underneath one another there. That became the third and last way of reading a number out of a table.
The refusal was the more important half of the day. “Moz” is unambiguous. “$M” is not, because the currency hides in the prefix. So the conversion table knows us$ m, c$ m, usd m — and no bare $m. It cost something immediately: one company’s rate of return went unbooked although the figure was printed, the row was unambiguous, and a silver price of 50 $/oz in the same table argued strongly for US dollars. In Canadian dollars it would have been about 68. Arguing strongly is not a statement.
The same run turned up something no single reading path could have shown. One report writes its results with a tilde — “~42 USD million”, “~17 %” — thereby saying itself that it does not vouch for the digit. The column reader refused it, correctly. The running-text reader took a clean 17 out of the very same quote and would have booked it as a measurement. Two paths, two answers to one line, and the more permissive one wins because it runs second. That is the dangerous direction, and it only showed up because one run put both paths side by side.
A list answers one question, and it is not the question that matters
81c7d72 22 Aug 2026
The vocabulary of resource classes had been in the code from the beginning: Measured, Indicated, Inferred, Measured and Indicated, Proven, Probable, Proven and Probable, Historical, Exploration Target. Nine values. A list answers exactly one question — is this word in it? It does not answer whether two numbers with different words beside them have anything to do with each other, and that is the question every comparison hangs on.
It was not even guarding membership. Measured against the actual book: 739 of 762 resource and grade rows carry a class, written in ten different spellings. “Measured & Indicated” 66 times, “M&I” 30 times, “Measured and Indicated” 11 times — one class, three groups for anyone grouping by it. And six rows carried a sum the standard forbids outright, adding inferred material to measured and indicated, whose basis is too thin to mix into one figure.
Four functions replaced the list, and none of them guesses. One resolves the ten spellings and reports an unknown one as an exit rather than a fallback. One decides comparability, splitting at the family rather than at the confidence. One decides whether two numbers may be added at all, and has three separate reasons for saying no. “Historical Inferred” resolves to Historical, not to Inferred: the weaker property decides, or a number without a qualified person walks into the system because a word happened to stand next to it.

Is the booked grade the grade that gets milled?
e3fa982 22 Aug 2026
The book held 127 gold grades and 102 silver grades, and not one of them said whether it applied in the ground or after mining. That is not a nicety. A vein half a metre wide, mined to a one-metre minimum width, reaches the mill with half its grade. One company stands in the book at 37.4 g/t and in its report with a minimum mining width of 1.0 m.
Four runs across the most recent reports produced four kinds of mistake, and in every one of them the sentence had been read correctly. What was wrong each time was whose number it was: a mining recovery of 98 % booked as dilution, a copper recovery of 75 % booked as mining recovery, a maximum width of 3.0 m read where the minimum of 2.9 m stood, a cement content of 5 % booked as recovery.
The term itself means two things. Underground, minimum mining width is the width a vein is mined to. In an open pit it is the room the excavator needs — one report means 1.0 m by it, another 60 m. Both in one column would not be imprecision, it would be a false statement. They are told apart by their surroundings, never by the size of the number, and where the surroundings say nothing the entry stays “unclear”. Feet stay feet: still no silent conversion.
One register for every closed vocabulary
a35f2aa 25 Aug 2026
By then the guard checked two vocabularies and lived inside the self-test of the company file. That was wrong twice over: it had to be extended once per vocabulary, and it sat in a file about something else, where nobody introducing a third would ever have looked for it.
There is now one register carrying eight of them: origin levels, coverage classes, examination subjects, grading axes, resource classes, study stages, drill buckets, metal codes. Thirty-eight checks cover all eight, and a ninth vocabulary costs one line. Three questions are asked of each: does every place that mirrors this list know the same values, is the pattern it is read with still intact, and do the data carry a value the source does not know? The third question found a silent error the same morning.
The metal codes are deliberately a subset and not a copy — the register carries nine metals, the drill estimate three. So what gets checked is the direction. A metal in the register and not in the estimate is correct. A metal in the estimate and not in the register would be invented.

“% of weight” now reads “% of valuation”
ab38d0d 25 Aug 2026
Beside every unmeasured axis, each file said the axis “would carry 19 % of the weight”. In a tool that reports resources in tonnes and ounces, weight is the wrong word for the share of a grade: the reader goes looking for the deposit and finds a methodology.
Only what speaks to a reader changed — the header, the coverage box, the grade line, the title sheet, the tooltip, and the same line on the command line. Weight stays where it is the declared technical term and does not stand as a percentage next to a resource; the field names keep it too. It is a small commit and it belongs in this story because it is the whole problem in miniature: one word, two meanings, one screen.
A vocabulary nobody uses is a vocabulary nobody corrects
3507c70 28 Aug 2026
The hand-kept list of deposit types and the register of type bands had drifted eight values apart. The cost of that was already sitting in the book as a finding, with nobody drawing the connection: one company’s flagship deposit is a Carlin system and carries the entire economics of its report, and the reading role booked “type not in the vocabulary”, explicitly on the grounds that the vocabulary did not know the type — while the band for exactly that type stood in the register right beside it. A band check failed to run although the band was there. Five more positions had the same problem under a different name.
How rarely it was used is what proves it had to drift: three rows in the entire book, two of them the exit value. A second version that is almost never touched is one that nobody ever corrects — and of two versions, the more accurate one is the one that has no effect. The vocabulary is therefore derived now, from the same file that carries the bands, and the check measures the construction rather than today’s contents.
Renaming needs a directory, and none of the three existing mechanisms was one. An old spelling on a booked row is not wrong, it is old. So the band register carries a renaming directory, old to new, with a date and a reason. It cannot drift, because nothing new ever goes into it: a rename is an event, not a state.

“7.71 million tonnes” went into the book as 7.71 tonnes
5ce8b06 28 Aug 2026
The path that reads numbers out of running text allows one describing word to stand between a number and its unit — “13,422 gold assays”, “486 rotary samples”. A scale word walked through the same gap: “an Indicated Mineral Resource of 7.71 million tonnes” bound 7.71 to the unit for tonnes. A factor of 10 to the sixth, the same class of mistake as reading “million lb” as “lb”.
What makes this direction expensive twice over is who gets punished for it. The agent that converts the unit correctly and offers 7,710,000 as its cross-check is told its cross-check does not match its own quote — a failure notice for doing the work right. The agent that copies the printed digit gets the booking.
And the guard I had built that same morning against factor-of-ten errors does not fire here. It watches for a disagreement between the code and the cross-check, not for agreement in the same error. Both said 7.71, and both meant millions.
It now refuses rather than converting: this path reads a number together with its unit and computes nothing, so the repair is a finding that asks for a source where the unit is written out. The list of forbidden scale words is deliberately a list of prohibitions and deliberately short. A missed scale books a wrong number; one binding refused too many only demands a better citation.
What it does tonight
It runs as a service on the home server and is reachable only inside my own network — there is no public address, and this page is the only place it is described. One command rebuilds the whole file: freeze the reach, grade every position, build the files, the comparison, the chapter values, the coverage, the portfolio, the outstanding work, the history, the watchlist.
The grade is deliberately not a forecast. It is the share of a company’s story that is currently carried by something I could read, over nine axes, and beside it stands the confidence: how much of the valuation held up at all. What is unmeasured counts neither for nor against; it falls out of the calculation and stays visible as an unmeasured axis. A position under three measured axes gets no grade at all, and the file says so in those words — that is a statement about the state of the investigation, not about the company.
As of 30 August the test suite stood at 126 check runs and 4,466 individual checks, and the counts are ratcheted: a run that suddenly finds fewer checks than last time turns the suite red, because the usual reason a check stops failing is that it stopped running.
Runs on its own
- prices, metal prices and share counts fetched on a timer, each with its own as-of date
- grading over nine axes, from booked facts only, with the confidence computed alongside
- currency held per figure — every value knows which dollar it is, and the switch only touches what brought its own
- self-tests with ratcheted counts, plus mutation probes that check the checks still fail when they should
- a weekly macro tracker and a drill feed, both reporting to my phone only on a change of state
Stays with me
- reading the technical reports and deciding what a sentence actually says
- every buy and sell — the tool has never placed an order and has no access to do so
- resolving anything the machine marked “unclear”, which is where most of the work is
- deciding what a vocabulary is allowed to contain before anything may be booked against it
No metered API keys in the reading path: prices come from a keyless source at daily close, and the report work runs through the subscription. The expensive input here is reading time, not tokens.




Where this goes
The next thing is the part I have been avoiding: measuring the grade against what actually happened. A grade that says how well a story is documented is easy to defend and easy to fool yourself with, because nothing ever contradicts it. A history now exists, so the honest step is to hold old grades against later outcomes and publish whether the thing has any predictive content at all — including if the answer is that it has none.
Until that measurement exists, everything above is bookkeeping: counts of what could be read, not claims about what a share will do.