A number in a paper
Provenance answers one question: how was this result produced? A claim asks the other one, and until now Ligase could not answer it: what is behind the number I published?
The asymmetry this closes
ligase methods is unusually careful about what it does not know. It names a
tool it has no reference for rather than leaving it out of the reference
list, because “a tool silently missing from a reference list lets an
incomplete methods section look complete”
(D24).
It names a sample identifier that is really a file name
(D26).
It names a step whose production it cannot account for, rather than printing
a paragraph with a silent hole in it.
Three different promises, and all the same one: the record names what it cannot account for.
Now look at what it was silent about. The paper says variants were called
above 1% allele frequency. The pipeline says min_freq = 0.05, because the
threshold moved in June and the sentence did not. Both statements are in
front of you, in the same terminal, and nothing connects them. The engine
will tell you that fastp has no citation and say nothing at all about the
number in your abstract — because nothing has ever told it what that number
is.
A claim is how it is told.
What a claim is
A sentence a paper states, and the step that produced it:
pipe = lg.Pipeline(
samples.map(lambda s: resistance(call(align(s, hxb2), hxb2))),
claims=[
lg.Claim(
"variants were called above 1% allele frequency",
step="call",
parameter="min_freq",
value=0.01,
),
],
)The sentence is yours and is never checked. Everything else is: that call
is a step this pipeline produces, that min_freq is a parameter it takes,
and that the run held it at 0.01. What comes back is the digest of what the
step produced, and the date that computation happened:
Why the sentence and the value are two fields
A paper writes 1%. The pipeline holds 0.01. Comparing the sentence against the parameter would be a guess about arithmetic in English, and a mechanism built on that guess would be wrong in both directions — calling correct papers wrong and, worse, occasionally calling a wrong one right.
So they are separate and each does one job. text is what you grep the
write-up for with the manuscript open beside it. value is what the engine
compares, written the way the pipeline holds it.
The three verdicts
Bound. Every check passed. The number rests on a computation this write-up accounts for, named by key.
Contradicted. Something stands behind the number and disagrees with it. This is the drift above, caught:
Both numbers are stated, so the fix needs no investigation — and the write-up does not guess which of the two is wrong, because it cannot know whether the paper or the pipeline is the thing that should move.
Unbacked. Nothing in the run stands behind it at all:
There are five ways to land here and the complaint distinguishes them, because a renamed step, a dropped sample, a renamed parameter and a swapped tool are four different one-line fixes. A claim whose step is a gap is the fifth: the step exists, its record does not, and a claim resting on a step nothing is known about rests on nothing.
Why a declaration, and not a guess
The engine does not open your VCF and count its rows. It could do that for the formats it knows and would have to skip the rest, which is the lenient-canonicaliser failure in a new costume: a mechanism that verified some numbers and silently waved the others through.
What it does instead is narrower and honest. A claim with a value is
compared. A claim without one is bound — to a digest, a key and a date, so
a reviewer with the bundle can find the computation and rerun it. Neither is
“the engine checked your arithmetic”, and the pages do not say it is.
The one thing a claim must never be is a sentence reprinted under a heading.
M13 put it in one line: a declaration nothing can check is a comment. A
Claims section that merely echoed your prose would make an unchecked
paragraph look checked, which is the exact failure D24 refuses in the
reference list — so every part of a claim except its text is checked against
the run, and a claim that cannot survive that is named rather than printed.
What a claim is bound to
The computation, never the occasion. A claim binds to the step key, and a key is already the identity of a computation: the same code, the same parameters, the same inputs, the same tool versions. So a step served from the cache backs its claim completely, and the date you see is when that computation happened — which may be months before the run that wrote the paper up.
This is deliberate and it is D45.
The alternative would make ligase methods --force the only honest way to
write up a paper, which is the opposite of what a content-addressed cache is
for.
What a claim is bound to and what it is cited by are then two different
things, and conflating them cost a consumer a test. The key is the identity
of a computation, so it moves when the computation’s code, parameters,
inputs or tool versions move — and cite= moves the key while the artifact
stays byte-identical and nothing reruns. The digest of the output’s
canonical form is what survives such an edit, and it is what audit
and a bundle verify against, so that digest is the identifier a write-up
prints and a paper quotes
(D59),
under the name that says which digest it is
(D64).
The same decision has a second half: a claim is in no cache key. Editing a sentence in your manuscript reruns nothing. A mechanism whose cost was a re-alignment per draft would simply not be kept up to date, which is the worst available outcome for a feature whose entire job is to be current.
And a claim may name where its number lives
Everything above checks a claim against the record: the step, the sample, the parameter, the tool version. The number itself was the one part that could not be checked, because nothing told the engine where it was.
at=lg.cell(column="f1", row=0) tells it, and then ligase methods reads
that cell and compares it against value= within a tolerance= the claim
states. The verdict is verified where they agree and contradicted where
they do not, and a contradicted claim exits non-zero — so CI fails when a
manuscript’s number stops matching its artifact
(D61).
Four things it will not do, and each is the same refusal in a different place. It will not go looking for a number in output it was not pointed at. It will not parse a format it was not told. It will not default the tolerance, because a claim to three decimals and a claim to machine precision are different claims. And it will not call a claim wrong because it could not find the cell — an unreadable location is reported as unbacked, which is “I could not check”, not “you are wrong”.
Where to go next
Binding a number you published is the task, with a runnable example. The number in my paper is not the number my pipeline produces is the failure, by symptom. The internals say how the checks are made and what they deliberately do not do.