How many
times did you
run it?
Someone tells you their model scores 0.87 on their internal eval, up from 0.81. You cannot check any of it. Not because they are lying, but because nothing about that claim is checkable. The quietest failure is not a moved threshold, it is nine runs and one publication. PLIMSOLL makes the attempt count a published fact instead of an optional disclosure. Spec: 10.5281/zenodo.22107451, released CC0. Code: plimsoll. Live log: plimsoll.gautamkhosla.com.
SEC. 01
The question almost nobody asks out loud
Did they pick the bar before running, or after seeing the number? Is the dataset the same one they used last quarter? And the one that matters most: how many times did they run it?
Run the eval nine times with slightly different seeds, temperatures or retrieval settings. Publish the run that cleared the bar. Omit the other eight. Nothing was falsified and every number is real.
Git commits do not catch this. Signed results do not catch this. A vendor's own eval platform structurally cannot catch this, because it belongs to the vendor.
SEC. 02
The mark on the hull
A plumb line is a weighted string that defines true vertical. The Plimsoll line is the mark on a ship's hull, set in advance by an independent authority, showing how deep it may be loaded. Anyone standing on the dock can look at the waterline and see whether the ship is overloaded. The mark is public, it was placed before the cargo went on, and it cannot be quietly moved.
Before you run, you write down your metric, threshold, dataset hash and decision rule. It is canonicalized, hashed, signed and appended to a public append-only log. Then you run your eval with whatever you already use. Afterwards you bind your results to the seal, and the engine applies only the rule you sealed.
The architectural commitment underneath is the part I would defend hardest: PLIMSOLL has no scorers, no metrics, no judge, no assessment framework. It never receives a dataset or a model. It is a notary, and that is permanent rather than a v1 limitation.
The verifier also points anywhere. It works against any conforming log, not just mine. A transparency log with exactly one operator is just a database with good manners, so if I vanish, go rogue, or start rewriting history, it is detectable by anyone holding the CLI.
SEC. 03
The attempt ledger
When you publish an attestation, the log assigns the attempt number inside a database transaction. The client cannot pick it. There is no API that sets it. A seal with five attestations shows five attempts, in order, with all five verdicts, forever.
That distinction between iterating and hiding iteration is deliberate and it is a design constraint, not a slogan. If the tool shames iteration, nobody publishes anything and the log dies empty. So the disclosure is mandatory and the presentation is neutral.
SEC. 04
There is no override
A sealed decision rule cannot be edited. Not with a flag, not with an environment variable, not with a config file, not on a paid tier. I went looking for a code path that mutates a sealed field and wrote a test that fails if one ever appears.
If your pre-registration turns out to be wrong, you issue a superseding seal. It references the old one, carries a required public reason, and every verification reports the whole chain.
This is the only reason a sceptical third party would accept the output at all.
SEC. 05
Determinism, all the way down
Comparisons use fixed-precision decimals from the original lexical form, never through binary64. A threshold of 0.82 comparing wrongly against 0.8200000000000001 is exactly the kind of bug that would destroy the tool's credibility, so the type system prevents it rather than a review catching it.
Dataset hashing treats a dataset as an unordered multiset of canonicalized rows, so reordering your JSONL does not raise a false "dataset changed" alarm, while duplicating a row does change the hash. Canonicalization is version-tagged so a future change cannot silently invalidate history.
Same seal plus same results equals the same verdict, on any machine, forever.
SEC. 06
What this has not shown
There is no field data. All three kill tests are unevaluable at N equals zero, and the entries currently in the public log are my own fixtures, authored and verified by the same person, against a five-row synthetic dataset. They count toward nothing.
That sits at the top of the README rather than in a footnote, because a tool whose entire purpose is making unstated iteration visible cannot itself round an empty log up to a result.
KT-2 is not evaluable: neither PASS nor FAIL. Writing anything else would be the exact behaviour this tool exists to make visible.
Three further limits are stated in the technical note rather than discovered by a reader. Digest-to-artifact correspondence cannot be verified by a system that declines to receive the artifact. A single-operator log admits equivocation without a gossip protocol. And no empirical study of the underlying phenomenon has been conducted, by me or as far as I can find by anyone else, so the premise that selective reporting occurs at a meaningful rate in machine learning evaluation is currently an assumption.
SEC. 07
Where this sits
PLIMSOLL is not a new idea. It is an old idea pointed somewhere new. Medicine solved this in 2005, when trial registration before enrolment became a condition of publication, because selective reporting was distorting what everyone believed to be true. Certificate Transparency solved the log half in 2013: make issuance public and append-only, and misissuance becomes detectable without anyone having to be trusted.
The lineage matters more than the novelty. Claiming this was invented here would be both false and weaker, because the prior art is the argument that the mechanism works.
The specification is released CC0 precisely so that nobody has to ask permission to implement it, and so that if I abandon the project someone else can implement it cleanly while existing logs remain verifiable.
SEC. 08
What broke, and what I would change
The publish path is asynchronous and the first version pretended it was not. A seal is not queryable until the log appends, which takes about a minute. Returning success on submit and letting a user assume inclusion would be a lie of exactly the kind this tool exists to prevent, so the CLI now prints what actually happened and offers an explicit wait flag and a poll command.
I split three packages for reasons that look redundant and are not. The Merkle arithmetic lives separately from the log because the browser verifier compiles to WASM and cannot link SQLite. A second, deliberately independent log server exists only so the test suite can prove verification works against a log that is not mine. If the tests could only verify my own implementation, the neutrality claim would be untested.
A reachable denial of service in the canonicalizer. Found and fixed during hardening. The canonicalizer is the first thing that touches untrusted input on the submit path, which is the worst possible place for it and the most predictable.
The single-maintainer problem is real and I did not argue it away. Current security guidance tells organizations to avoid depending on single-maintainer projects for core functions, and that guidance is correct. The answer is not to ask for trust: the log is clonable and offline-verifiable, releases are signed with cosign keyless and carry SLSA provenance, the dependency surface is two direct dependencies, the spec is CC0, and the verifier already points at any log.
What I would change: run the empirical study first. The premise is that selective reporting happens often enough in machine learning evaluation to be worth a log, and I built the log before measuring the phenomenon. KT-2 is essentially that measurement deferred until other people show up, which means the project's own evidence depends on adoption it has not earned yet. Building the instrument before the observation is the same ordering mistake I have now made twice.