How the other side checks the answer
The claim this product makes is not trust us. It is that an opposing party can take the same files and check the analysis without trusting anybody.
So the verifier is about a hundred readable lines at the bottom of
forensic/evidence.py rather than buried inside the command-line layer,
deliberately: a hostile reviewer should be able to read the whole verifier before
running it.
There is nothing to download from this site, and no artefact whose integrity you need to check before installing it. Nothing has been published under any terms and nothing is distributed here. This page is about verifying a report, which is the thing that ends up in a dispute. If you want to run the tool, that is what a call is for.
Record what was run
construct record baseline.xer update-11.xer --json --out record.json
--json is not optional here. record prints a Markdown report by default,
which is for a person; the record verify reads is the JSON one. Handing the
report to verify is the ordinary first mistake, and it says so rather than
failing on a parse error.
The record binds three digests:
- the bytes as received — SHA-256 over the input, streamed, before anything is parsed;
- the network parsed out of them — so that a change of engine version cannot make the same schedule look like a different file;
- every date, float and flag computed from it.
The third is the one that matters. A record without it supports only "we both read the same file".
construct record --json writes the same answer into the report itself, so the
document the client paid for is the file the other side verifies. There is no
second artefact to explain.
Reproduce it
construct verify record.json baseline.xer update-11.xer
WARNING REPRO.METHOD.UNCHECKED the record's method states command='evidence record',
files=['p6-23.12-xval.oracle.xer'], and this verifier does not check any of it
WARNING REPRO.CONFORMANCE.ABSENT this record binds the computed schedule and states
no conformance verdicts, so nothing here re-derives a PASS, a FAIL or an abstention
REPRODUCED: every value this verifier checks was recomputed from these files.
2 note(s) above say what it did not check, or what differs without changing the answer.
The manifest digest is computed by whoever wrote the record and signed by nobody, so
it detects a record altered after it was written, not a record its author wrote to say
what they wanted; and nothing here establishes when it was written or that these are
the files in dispute.
Exit 0.
Note what the tool volunteers on success. Two warnings naming things it did not check, and a closing sentence disclaiming the strength of its own result. That paragraph is printed when the verification passes, not when it fails.
verify takes no --terms, --jurisdiction or --baseline. That is
deliberate: an option supplied at verification time would let it answer a
different question from the recorded one, which is precisely the thing it exists
to detect. --project must be given again, because it changes every digest and
is written into the record's own method.
Exit 0 reproduced, 1 diverged, 2 the record or a file could not be read.
Exit 1 is not an accusation. The commonest cause is a different engine
version, which the manifest names; the second is reproducing against a different
file, which the input digests name. Both are stated before any date is compared.
What a divergence looks like
One field of a record altered and nothing else — activity 9202's early finish,
2026-01-16 changed to 2099-01-01:
ERROR REPRO.ANSWER.FIELD activity '9202', early_finish: the record says '2099-01-01',
this run computed '2026-01-16'
DID NOT REPRODUCE: 1 difference(s) above. Those about the answer name the activity and
the field where the two runs disagree; those about the manifest name the field of the
record that does not stand up.
Exit 1.
The failure names the activity and the field. "The digests differ" is useless in a dispute, because it tells neither side where to look.
Note also what did not fire. The answer-digest check is silent, because the record's stated digest still matches the digest of this run's answer: the body was altered and its own digest was not. That is a different attack from a failed reproduction, and the row-by-row comparison is what catches it. The two checks are not redundant, and each was verified by introducing the fault it exists to catch and confirming it fires.
The check that needs none of our code at all
This is the strongest thing on the page, and it is the one most likely to be used against us.
An XER exported from P6 already contains P6's own answers. So an opposing expert does not need to run this engine, and does not need to write a scheduler:
Take the stated answer table. Take the corresponding fields off the
TASKrows of the XER we were given —early_start_date,early_end_date,late_start_date,late_end_date,total_float_hr_cnt,free_float_hr_cnt. Compare, activity by activity, field by field.
Run that comparison and roughly one field in three will differ, because this engine agrees with what P6 stored on 67.85% of 584,687 field comparisons.
That is published here rather than discovered there, and the disagreements are itemised, located and disclosed by us. It is also, plainly, a thing that could be used against us — which is the argument for saying it first.
The words used here, and the ones that are not
The record is append-only, hash-chained and independently verifiable.
It is not tamper-evident, and this site will not use that phrase. No timestamp authority is wired up: the timestamp interface is an injected callable with no implementation shipped, and a record this engine produces asserts no time about itself. The report says so in its own words, on every run:
Date prepared is a self-asserted local time, read from the clock of the machine that ran this analysis and checked against nothing. It records when this document was produced. It is not evidence that this document, or the files it describes, predate any dispute; only an external timestamp authority is, and this engine wires none up.
There is no verified: true anywhere and there will not be. A format that
hides a limit is worse than none, and a top-level boolean is exactly that.
Certification.to_dict reports "signed": false unconditionally and no code path
in the engine may set it otherwise, because an FRE 902(13) certification is a
sworn statement by a qualified person and not a field. What the engine emits is
draft text for such a person to review, adopt and sign, and it is not a
certification until they do.
Self-authentication under FRE 902 addresses authenticity only, not hearsay, and 902(11) requires reasonable written notice — missing that step forfeits the benefit.
Five things no record can establish
Stated plainly, because they are what a cross-examination will find:
- The input file may not be the one the contractor submitted.
- The record may not predate the dispute.
- The engine may be wrong — in which case two runs of wrong code agree and
the verifier prints
REPRODUCED. Reproducibility is agreement between two runs of the same code. What bounds that is the agreement measurement, not anything on this page. - The parties may not have run the same engine.
- The analysis may answer the wrong question.
REPRODUCED is a good word precisely because it does not mean correct.
And one thing that has never been checked at all
Determinism has been measured across five fresh processes under five explicit hash seeds — on one machine, one operating system and one interpreter. The cross-platform and cross-version matrix has never executed once in the life of the repository.
So: cross-version determinism is unknown, and cross-platform determinism is assumed. That does not mean the engine is non-deterministic. It means nobody knows, and for a product whose central claim is the same input gives the same answer, the difference between unmeasured and fine is the whole subject.
It is also the reason this site distributes nothing. A build that has only ever been observed to produce its answer on one laptop has no business being handed to somebody who will rely on it in a hearing.
Source: web/pages/verification.md. Source commit date: 2026-09-06.