Getting started
This page is for a scheduler or project controls manager with a programme in
front of them, not for a programmer. It assumes you have installed the tool by
following docs/INSTALL.md and that construct --help prints something.
Everywhere below, construct is the command. If your installation does not put
construct on the path, python -m forensic is the same program and takes the
same arguments.
What the tool does, in one paragraph
You give it one or more schedule files. It reads them without opening P6, works out the dates and floats itself, and reports what the specification or standard that governs your contract says about what it found. It reports three kinds of answer and keeps them apart on purpose: what it decided, what it will not decide because a person has to, and what it could not evaluate because an input it needs was not supplied. It never says the submission is approved or rejected. That determination belongs to the reviewing authority, and the report says so at the top of every run.
What it reads
Primavera XER, Primavera XML and Microsoft Project MSPDI files. The format is
detected from the contents of the file, not from the extension, so a P6 XML
export renamed to .mspdi is still read correctly.
It does not read a binary .mpp. Export from Microsoft Project as XML instead.
It never writes a schedule file. Nothing you give it is modified.
If you would rather not use a terminal at all
construct serve
That prints an address — http://127.0.0.1:8765/ — and opens it. Everything
below can be done from that page instead: point it at a schedule file already
on your computer, read the report, read the list of clauses it could not decide
grouped by the one thing you would have to go and find, supply that and run it
again, and save the result where you choose. It binds 127.0.0.1, which no
other machine on any network can reach; nothing is uploaded and no page fetches
anything from the internet. Press Ctrl+C in the terminal window to stop it.
It is a shell over the commands below and answers nothing they do not.
engine/quality/THE-INTERFACE.md says what it deliberately does not show.
The first thing to run
construct my-schedule.xer --jurisdiction us_federal
construct my-schedule.xer on its own is the same as construct check
my-schedule.xer, so the word check is optional.
--jurisdiction says what governs. The choices are us_federal for USACE,
NAVFAC and agencies that adopt UFGS; us_state_or_private for state DOT,
municipal and private work; uk_scl for UK and Commonwealth practice under the
SCL Protocol; and neutral, which is not "no standard" but "nobody has told me
yet" — under neutral the report states in writing that its authority was
assumed. If your contract names a governing document by title, name it with
--contract instead, repeated in the contract's own order, because
incorporation by name is a contract term and beats any default.
You get a Markdown report on screen. To produce something to attach to a submittal response, write it to a file:
construct my-schedule.xer --jurisdiction us_federal --out response.html
A filename ending .html produces a page you can open in a browser and print to
PDF. Anything else is Markdown.
What comes back, and in what order
The report opens with a reservation of determination, then the method, the governing documents, any conflicts between them, and the assumptions and limitations of the run. That is roughly the first eighty lines and it is there so the report survives being read by the other side.
After that comes an inventory of the schedules considered, including what the importer could not take literally — a file with no terminator, a calendar with part days, a missing relationship table — and then the findings themselves, grouped by what kind of answer they are:
- Blocking failures. A stated condition of a cited clause is not met.
- Observations. Something is stated but not scored.
- Referred. The evidence is assembled and a person must decide.
- Not evaluated — one input away from deciding. A clause turns on a contract term that was not supplied, and the report names the term.
Every finding names the document and the clause, quotes what the clause requires, and states the condition found. Every finding is also tagged with what it rests on: from your file means it would still stand if this tool's date arithmetic were wrong, and from our arithmetic means it would not.
Exit codes are the same across every command: 0 means nothing blocking was found, 1 means there was a blocking finding or a withheld result, and 2 means the inputs could not be read or were refused.
Reading "not evaluated" and doing something about it
This is the section that turns a thin report into a useful one, and it is the part most first-time users stop at.
A clause like "the schedule shall complete no later than the contract completion date" cannot be checked against the schedule alone, because the contract completion date is not in the schedule. So the tool abstains and names what it wanted:
UFGS-059 §3.3.21 — needs terms['contract_completion_date']
You answer it with a small JSON file of contract terms and a --terms
argument:
construct my-schedule.xer --jurisdiction us_federal --terms terms.json
The file is a single JSON object whose keys are the names the report gave you.
Dates are written as YYYY-MM-DD. Nothing else about the format is required.
The loop is: run without --terms, read the "not evaluated" list, write the
keys it names into a file, run again. Each pass converts abstentions into
decided findings. There are worked files to start from in examples/ —
examples/terms.min.json is two keys, and examples/terms.us-federal.json is
what a US federal review typically needs. examples/README.md gives the exact
command line for each.
Two things to know before you write one. A key the tool does not recognise, and
a date in a format other than YYYY-MM-DD, each produce a warning naming the
key and suggesting the nearest real one — but the warning goes to the terminal
and the run continues, and the clause still reports the term as not supplied. If
a clause keeps asking for something you are sure you gave it, the answer is in
those warnings. And where a term is a list of activities, the ids are the ones
the report prints, which for a P6 export are the task_id values from the
database and not the Activity IDs the P6 window shows you.
"I have a schedule and a rejection letter. What do I run?"
Four commands, in this order.
One. Check the schedule against the specification.
construct update-11.xer --jurisdiction us_federal --out findings.html
This is the list of clause-level defects, and it is the same list the reviewer's comments are drawn from. Read it beside the letter. Where the letter cites a clause, the report will have something to say about that clause or will tell you which contract term it needed to have an opinion.
Two. Validate the schedules the argument will rest on.
construct validate baseline.xer update-11.xer --baseline 0 --jurisdiction us_federal
Source validation asks whether the baseline and the updates are fit to be
analysed at all — before any delay analysis is worth doing. --baseline 0 says
which of the files you listed is the contract baseline, counting from zero, so
0 is the first file named. The tool never guesses this, because the earliest
file is very often a superseded plan.
Three. If time is in dispute, run the analysis the contract or the standard calls for.
construct analyse baseline.xer update-11.xer --method 3.6 --baseline 0 --delays delays.json
--method takes an AACE RP 29R-03 section number, or a common name where the
name means exactly one method. "TIA" means either MIP 3.6 or MIP 3.7 depending
on a choice the name does not carry, so the tool refuses it and asks the
question that separates them rather than picking one.
Methods 3.6 through 3.9 model delay events into or out of the network, so they
need a --delays file describing those events: an identifier, a name, a
duration in days, and the activity each one bears on. Methods 3.7 and 3.9 also
need the date each event started. examples/delay-events.json is a worked file
and examples/README.md has the command line.
Four. If the dispute is about whose delay came first, run the concurrency command.
construct concurrency update-11.xer --jurisdiction uk_scl --delays periods.json
This one reads a different kind of file: delay periods, each with the party who bears the risk, when it happened, and — separately — the window over which it actually drove the critical path, which is often not the same thing. The tool reports the answer under all four combinations of the two concurrency theories and the two causation doctrines, states which one the governing document makes binding, and where nothing governs it reports the spread rather than picking one, because the standards genuinely disagree here.
You supply the party. The tool computes the overlap. If you do not know whose
delay it was, write undetermined — that is a real answer, and it keeps the
period out of every pairing and lists it as an open question instead of filing
it against somebody. examples/delay-periods.json shows both.
Looking forward instead of back: a time impact analysis
Where you are pricing an extension of time on a change that has not yet been
absorbed, the prospective procedure is AACE RP 52R-06 and the command is tia.
You supply a fragnet — the small network of added activities and the
relationships tying them into the programme — as a JSON file, and the day the
delay began.
construct tia update-11.xer --fragnet fragnet.json --delay-start 2026-09-14 \
--unit work_days --reviewed 0
The tool walks the eight steps of the recommended practice and records, for each
one, what it did, what it assumed, and whether the tool decided it or a person
did. Three of the arguments have no default and will stop you if you leave them
out. --unit is work days or calendar days, and the two answers differ by
roughly forty per cent on a five-day calendar. --reviewed names which of the
files you listed the owner actually reviewed, because Step 2 requires the base
schedule to be the last owner-reviewed update and that is a fact about the
project record. And the fragnet's own accepted flag is Step 1: it defaults to
false, which refuses the analysis, because the owner's acceptance of a fragnet
is a claim the analyst makes rather than a box this tool can tick.
examples/fragnet.json is a worked fragnet.
Recording what you were given
construct record baseline.xer update-11.xer update-12.xer --json > record.json
This produces a provenance record: a digest of the bytes as received, a separate digest of the parsed network so that a change of engine version cannot make the same schedule look like a different file, and a list of what is observably absent from the set — missing update numbers, unusual intervals, a baseline dated after the updates it supposedly precedes. Those are reported as observations about the documents in hand and never as a finding that anything was withheld.
Keep this beside the reports. It is what lets somebody else re-run your analysis on the same bytes a year later and get the same answer.
Which commands need a JSON file, and why
check, validate and record run on a schedule alone. They will run better
with --terms, but they run.
analyse with methods 3.6 to 3.9, concurrency, and tia cannot run without
one, and this is deliberate rather than an omission. Which delays happened, when
they bit, whose they were, and what a fragnet contains are all facts about the
project record. They are not in the schedule file, and a tool that inferred them
would be answering the disputed question by itself. So they are read from a file
you write, and the report states them as your input rather than as its finding.
That is also why the tool refuses a file it does not fully understand instead of
ignoring the part it cannot read. --delays and --fragnet reject an
unrecognised key and list the ones they accept, because a key nothing reads is a
value nothing applied.
Every one of these files has a worked example in examples/, minimal and
realistic, with the exact command line above each.
Where to go next
examples/README.md— the input files, the command line for each, and the three details about activity ids that cost an afternoon if you meet them first in an error message.docs/INSTALL.md— installation, and the four ways it usually goes wrong.docs/CONFORMANCE.md— every rule the tool holds, by document and clause.docs/METHODS.md— the AACE methods, and which of them are runnable today.docs/LIMITATIONS.md— what this tool does not do and what it has not been tested on. Read this before you rely on a number.
Source: docs/GETTING-STARTED.md. Source commit date: 2026-09-06.