Installing it, and the first thing to run

For somebody who has not run this before. It ends with a report on screen and knowing which document explains it. Every command on this page was run on Windows before it was written down. Where a command can fail, the failure is named rather than left to be discovered — the failures in section 2 are the ordinary ones for a Python install and are not all reproduced here.

The package is not published. pip install construct-engine and pipx install construct-engine resolve to nothing — the name was checked and is unregistered, which RELEASING.md (RELEASING.md) §9 records with the date and the HTTP status. Installing means installing from a clone, and the rest of this page assumes you have one.

1. What you need

Python 3.11 or later, and nothing else. The engine has no runtime dependency of any kind and that is enforced by an import contract rather than by a convention, so there is no wheel to compile, no virtual-environment lock file to reconcile, and no network access needed after the clone.

That last clause is true of the engine and not of the validation suite, and the distinction matters enough to state here rather than to let you find it. Running the tool needs nothing off the network. Re-running the evidence does: the corpus, the PSPLIB instances, the structural fixtures, MPXJ and the two other comparison engines are all gitignored and fetched by pinned, hash-verified scripts, because most of their sources state no licence — which is a finding and not a permission. On a clean clone with nothing fetched, 118 of the suite's collected items skip, and python check.py still prints all 6 checks passed. engine/quality/THE-REPOSITORY.md section four is the table of what runs, what skips and what each skip costs you.

Check what you have:

python --version

If that says 3.10 or earlier, or python is not found, install a current Python before going further. On Windows the launcher is py -3.11 and on macOS the system Python is usually too old.

2. Install

From the top of the clone:

cd engine
pip install -e .

That puts a construct command on your PATH. It is the same entry point as python -m forensic, which is the form most documents here use; neither is a wrapper around the other, and every command on this page works either way.

If you would rather install nothing, set PYTHONPATH to engine/src and use python -m forensic.

When it does not work

Confirm it is there:

construct --help

3. A schedule file to run it on

You need a schedule before any of this means anything, and the constraint on which files this repository may publish is real: most of the corpus comes from repositories that state no licence, and an absent grant is not a permission. Two files are in the clone and both may be shown to anyone.

engine/oracle/cases/p6-23.12-xval.oracle.xer — real Primavera P6 23.12 output, captured from P6's own database, transcribed from an MIT-licensed source. It is the file the whole of demo/README.md (../demo/README.md) runs on. It is a cross-validation export holding thirteen projects, XV01 to XV13, of 27 activities between them — so every command below names one of them with --project. XV01 is a three-activity chain: it will tell you the tool runs and nothing about how it behaves on a programme.

A file holding several projects has to be told which one you mean. check reviews one schedule, so given thirteen it lists them with their activity counts and asks, rather than reviewing the first and mentioning the other twelve in a note. index is the command for the other reading: it reviews every project in the file, one row each, and needs no --project — so construct index engine/oracle/cases/p6-23.12-xval.oracle.xer works as it stands. Naming a project narrows index to that one too, if that is what you want.

demo/outputs/synthetic-800.xer — 800 activities, three calendars, a two-level WBS, progress on the first third. It is generated, by the same function the performance measurements use, and it says so in its own project name: P6 would show the project as SYNTHETIC. Use it to see what the output looks like at a size you recognise. Do not read the counts in its findings as anything about real schedules — demo/README.md §5 sets out precisely what a generated file can and cannot demonstrate.

Your own file works too. XER, Primavera XML and Microsoft Project MSPDI are all read, and the format is detected from the contents rather than the extension.

4. The first command

Section 2 left you in engine/; these paths are from the top of the clone, so go back up first.

cd ..
construct engine/oracle/cases/p6-23.12-xval.oracle.xer --project XV01 --jurisdiction us_federal

A conformance report on standard output. --jurisdiction is the argument that matters most and the one with no safe default: it decides which standards govern, and therefore which clauses are even looked for. Run the same file under us_state_or_private and you get a different report from identical bytes.

Two things about what comes back will look wrong and are not:

Add --out report.html for a document to attach to a submittal response and print to PDF from a browser. The Markdown and the HTML are rendered from one object, so they cannot disagree.

4a. Turning that list into a worklist

The NOT_EVALUATED list is the longest section of that report — on XV01 under us_federal it is 79 of the 120 clauses that apply — and it is printed one clause per line, in citation order. Read straight through, it gives the impression that the tool mostly cannot answer. What it is really telling you is much shorter, because those 79 lines name far fewer distinct inputs between them and several of the inputs are wanted by a dozen clauses each.

unanswered is the command that does that arithmetic. It takes the same arguments as check:

construct unanswered engine/oracle/cases/p6-23.12-xval.oracle.xer \
    --project XV01 --jurisdiction us_federal

It groups the abstentions by the input a person would have to supply, largest group first, so the top of the list is the most useful thing you can go and find. On the run above the first line reads that supplying the delay events alone would make twelve clauses decidable. It says decidable, not passing — a clause that can be decided can also fail — and it separates the inputs that settle a clause on their own from the ones that need a second input as well.

This is the page to work from when you assemble the terms file in section 5, rather than working from the 79 lines by hand.

clause CLAUSE answers the same question for a single clause without running anything: what it asks, what it looks at, and what input would let it be decided.

health FILE is the other command that needs no contract terms at all. It runs the DCMA 14-point screening metrics — logic, leads, lags, relationship types, hard constraints, float, durations, dates, resources, missed tasks, the critical-path test, CPLI and BEI — and reports which of the fourteen could not be run and why, rather than scoring them as passes. It is not a conformance check and never exits 1: GAO-16-89G appendix VII states that DCMA's thresholds "are not compliance triggers", so a red metric here is a prompt to look, not a finding. Where a specification does mandate one of these quantities, check runs it as a cited clause at that specification's own threshold, which is usually not DCMA's.

construct health engine/oracle/cases/p6-23.12-xval.oracle.xer --project XV01

5. The second command, which is where it gets useful

The NOT_EVALUATED list is a worklist. Each entry names the contract term it wanted. Put those in a JSON file and run again:

construct engine/oracle/cases/p6-23.12-xval.oracle.xer --project XV01 \
    --jurisdiction us_federal --terms terms.json

The coverage figure on the score line rises and the NOT_EVALUATED list gets shorter; the clauses that moved are the ones whose terms you supplied, and they are named in both lists so you can check that they are.

~~The full key list~~ What each key means, the date format, and the difference between an absent key and an empty list are in CONFORMANCE.md §5, which also gives a minimal example to copy. An empty list means none were made; an absent key means nobody said, and the tool treats those differently on purpose. §5 covers three of the thirteen packs and 44 of the 144 keys the tool accepts, so the list is your own no-terms run — the Not evaluated section names every key that clause wanted. Corrected 6 September 2026.

If you have more than one dated snapshot, pass them oldest first and name which one is the contract baseline with --baseline 0. It is never guessed — READING-A-REPORT.md §9 measures what the second snapshot actually buys.

5a. Proving the answer to somebody who does not trust the tool

Two commands, and they are a pair. record writes down what was received, what was parsed and what was computed; verify re-runs the files against that record and either reproduces the answer or names the first value that differs.

construct record engine/oracle/cases/p6-23.12-xval.oracle.xer --project XV01 \
    --json --out record.json
construct verify record.json engine/oracle/cases/p6-23.12-xval.oracle.xer --project XV01

--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.

verify takes no --terms, --jurisdiction or --baseline, and that is deliberate: it re-runs what the record says was run. An option supplied at verification time would let it answer a different question from the recorded one, which is the thing it exists to detect. --project must be given again, because it changes every digest and is written into the record's method.

Exit 0 means the answer reproduced, 1 that it diverged, 2 that 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, and the second is reproducing against a different file, which the input digests name; both are stated before any date is compared.

  1. CONCEPTS.md — the ideas the output is built on.
  2. READING-A-REPORT.md — the report walked through section by section, on a real one.
  3. LIMITATIONS.mdbefore you rely on a number. Every report prints the agreement measurement itself; this is the full list, and it is longer.

demo/README.md (../demo/README.md) is the shortest route to seeing what the tool does without installing anything: five sections of commands and their real output, the last of them at 800 activities.

Source: docs/INSTALL.md. Source commit date: 2026-09-06.

See it in practice

Follow the evidence, from the schedule to the finding.

Explore the worked example