Five jobs, end to end
Written 3 September 2026 by walking each job at the command line. Every command below was run; every figure is read off its output. Where a workflow stops, it says so and names what would move it.
The command line has been built one subcommand at a time and nobody had written down the journeys it is supposed to serve. This document is those journeys. It is not a feature tour: the useful half of it is where each one breaks, because that is the part a buyer discovers on their own otherwise.
Read GETTING-STARTED.md first if you have never run the tool. This assumes it
is installed and that construct is on your path — everything below also works
as python -m forensic from a source tree, and the help text names whichever one
you typed.
One fact before any of them. On the measured corpus — 66 files when this was
measured, 62 since 2026-09-04, because a file holding several projects is now
refused rather than reviewed as its first one (engine/quality/VERDICT-YIELD.md
§13.1) — a single schedule file with no contract terms
supplied answers a median of 45 clauses, 13 of them a FAIL or an OBSERVATION,
and leaves 75 listed as "Not evaluated", each naming the one input that would
settle it. That is the shape of workflow 1 and it should be stated rather than
discovered. (Counting REFERRED as adverse too, the middle figure is 22 rather
than 13; the sentence used to say "adverse" and mean the narrower thing without
saying so.) 116 of the 351 registered rules — 33% of the catalogue — cannot
answer from a schedule file at all. They want a number from the contract or a
declaration from the analyst. A buyer who supplies nothing but an XER is buying
the other 220. That pair was ~~122 of 331~~ until 6 September 2026, when the
numerator was re-measured rather than re-denominated;
quality/VERDICT-YIELD.md §3.3 has the run. The companion figure — 83 still
silent once every contract term we ship an example of is supplied — was
measured over 331 rules and has not been re-measured, so it is not restated
here.
And what those terms buy is answers, which is not the same as insight. Every
input a reviewer can supply adds 68 rules that return a verdict and 8 that
return a different verdict on different schedules. The agency specifications
are where the difference lands — their terms are approval registers checked
against your own activities, and 642 of the 972 new adverse findings the
state-DOT terms produce vary from file to file. The doctrinal protocols are not:
the UK SCL terms take 19 answering rules to 46 and leave the discriminating
count at 12, because those clauses interrogate the analysis rather than the
file (engine/quality/VERDICT-YIELD.md §8.4, §9.1).
Contents
- A DOT reviewer, baseline submission
- The same reviewer, update 7 of 30
- A contractor's scheduler, before sending
- A forensic analyst, baseline against as-built
- An expert being deposed, reproducing a record
- What is common to all five
- Where the whole surface stops
1. A DOT reviewer receives a contractor's baseline
The question: is this baseline acceptable? The tool does not answer it, and says so at the head of every report. It states what the file contains and what the cited clauses say. The determination is the reviewing authority's.
The commands, in order
construct check BASELINE.xer --baseline 0 --jurisdiction us_federal
construct validate BASELINE.xer --baseline 0
construct record BASELINE.xer --supplied-by "Contractor" --received-at 2024-02-01
Worked on claim-01-baseline.xer from demo/claim-outputs/ — 18 activities, 20
relationships, data date 2026-03-02. Runtime about three seconds.
Why that file and not a real one. The corpus schedules this project measures
against were harvested from repositories that mostly state no licence, which is
the absence of a grant rather than a permission
(engine/oracle/corpus/SOURCES.md), so no report derived from one is published
here and no corpus file is named. demo/claim-outputs/ is written by
demo/claim.py from engine/tests/xerfuzz.py:claim_scenario, which builds every
table from literals in this repository: it derives from no real project, no
client file and no third-party dataset. It is a small file, and the numbers below
are correspondingly small — that is the honest cost of a worked example anyone
can regenerate.
What check returns and what it supports
21 passed, 6 failed — 78% of the 27 clauses that were scored, and those 27
are 22% of the 120 clauses that apply. Of the rest, 4 were stated as
observations rather than scored, 10 need a person and 79 could not run.
Exit 1, because all six failures are blocking. The six are the review: two
missing mandatory activities (§3.3.8.1, §3.3.8.2), three open-ended logic defects
under §3.3.11 naming MOBILISE, OPEN and STRIPE, a baseline carrying an
actual date on MOBILISE (AACE 29R-03 §2.1.B(3)), and no contemporaneous update
supplied — which two clauses report separately, ASCE 67-17 §4.2 and AACE 29R-03
§2.3.B, because they lose different things by it. Each names the activity by
the Activity ID P6 displays, so the reviewer can find it in the software
without a lookup.
That is a rejection letter's worth of material, and it is the whole of what one file supports.
Three things --baseline 0 and --jurisdiction buy, measured
With neither flag the jurisdiction defaults to neutral: 107 clauses apply, 9
are scored, 86 could not run. With --jurisdiction us_federal --baseline 0:
120 apply, 27 are scored, 79 could not run. Naming the two facts the tool
cannot infer is worth eighteen scored clauses and thirteen more applicable ones
on this file.
Naming the baseline is not a formality: --baseline is never guessed,
because the earliest file supplied is very often a superseded plan, and
promoting it silently would measure every variance from the wrong origin. Two
AACE 29R-03 clauses and the whole of the AACE 53R-06 update-review checklist sit
behind it.
Where it breaks
79 clauses could not run, and that is the largest section of the report.
Every one names its missing input. They are not defects — VERDICT-YIELD.md
§3.1 established that zero of 21,846 rule-file pairs abstained for a reason
internal to the tool, and it is still zero of the 20,522 the smaller population
produces (VERDICT-YIELD.md §13) — but a reviewer opening the report sees a long list of
things not checked and has to work out that it is a shopping list.
The seventy-nine divide into two kinds:
- Seventy want a value from the contract, the specification or the delay
narrative —
terms['pay_period_thru_date'],terms['mandatory_activity_names'],terms['interim_completion_dates'],asce.delay_events. Supply them in a JSON file with--terms. This is by a distance the largest lever on the page, and it is measurable: the same file, same invocation, with--terms examples/terms.us-federal.jsonscores 48 clauses instead of 27 and leaves 52 unevaluated instead of 79. Failures rise from 6 to 16, which is the file working rather than breaking — those sixteen were always true and the tool could not see them.
Start from examples/terms.us-federal.json, examples/terms.state-dot.json
or examples/terms.uk-scl.json, which carry every key their pack reads with
the clause that wants it beside each one; examples/terms.min.json is the
short version. A key nothing reads is complained about on stderr, with the
near match, and a key stated twice in the same object is refused outright
rather than resolved to one of its two values.
* The other nine want a second schedule, which a baseline submission does not have
by definition. They are correctly silent and workflow 2 is where they answer.
--respond is what turns that list into a next action. It regroups the same
findings by what the recipient has to do with them — nothing is recomputed and
no verdict changes:
construct check BASELINE.xer --baseline 0 --jurisdiction us_federal --respond
On this file that produces Change in P6 before resubmitting (6), Inputs you can supply (79), Questions for the narrative, not the file (10) and Nothing to do (25). It is written for the contractor and is the section a reviewer can forward without editing.
What validate adds, and why it comes second
Source validation under AACE RP 29R-03 §1.5.B(1) is the precondition for float and criticality meaning anything. On the same file:
2 passed, 2 failed of 14 source validation checks.
Not scored, deliberately: one failure can make every number computed from the
schedule unusable, so a percentage would average away the finding that matters.
The failures are SVP-2.1.B.3-PROGRESS — one activity in a baseline carrying
actual dates — and SVP-2.1.B.5, MOBILISE with no predecessor. Two protocols
are referred to a person (is the level of detail adequate; did the calendars
represent the expected working days) and eight could not run, six of them for
want of a terms key and two for want of a second dated update.
Exit 0 here even though a protocol failed, because none of the failures is
blocking. --strict also fails on a protocol that could not run.
What record adds
A file digest over the bytes as received, a model digest over the parse that
excludes everything computed, and a reproducibility manifest. --supplied-by
and --received-at are free text and a date you supply: the received date is
never read from the system clock, because a wall-clock reading taken at
analysis time would be a fabricated fact. Exit 1 if the set of documents has an
observable hole in it.
The workflow's own limits
- Whether the schedule matches the work is not a question a schedule file can answer, and no clause here pretends otherwise.
- The engine agrees with Primavera P6 on 67.85% of 584,687 measured field comparisons. Every report says so, and marks each finding from your file or from our arithmetic so a reviewer knows which of the two a given line rests on. Recalculate a date or a float in P6 before relying on it.
- A schedule with circular logic produces no report at all. It is refused by
name, with the loop written in Activity IDs and a sentence saying the silence
is not a pass — but there is no conformance verdict, and
UFGS-071, the catalogue's only clause about circular logic, is never reached. See §7.
2. The same reviewer receives update 7 of 30
The question: is this a valid update? This is the workflow the tool is best at and the one its documentation under-sells.
The commands
construct check UPDATE-06.xer UPDATE-07.xer --baseline 0 --jurisdiction us_federal --review
construct validate UPDATE-06.xer UPDATE-07.xer --baseline 0
construct explain UPDATE-07.xer --activity <the activity that moved>
Files are given oldest first, and the order is an input. check reads them
as successive versions of one project. If they are unrelated submissions, that
is index, not check, and the tool will not guess which: activity-code overlap
runs high between unrelated jobs and low between genuine updates, so a guess
would sometimes report several jobs as one job's history, silently and plausibly.
What the second file is worth, measured
Same invocation, --jurisdiction us_federal --baseline 0 --review, on
harbour-point-24.12 baseline and as-built:
On the later file alone, 181 clauses apply, 68 are answered and 113 could not run. On both files given oldest first, 183 apply, 88 are answered and 95 could not run.
Twenty clauses move from abstention to a verdict on the second file. They are the ones whose subject is change: what was added or deleted, whether original durations moved, whether activity descriptions were rewritten, whether the controlling path changed at the update boundary, whether the update cadence meets the specification.
--review is doing a lot of that work and it is worth knowing why: it elects the
AACE schedule-review recommended practices, of which 53R-06 is the update-review
checklist — twelve rules that exist only for this workflow. Without --review
the same pair gains nothing measurable, because the update-review clauses are
never applicable. They are elected rather than added to the precedence: they
apply to any schedule and govern none, and the report prints them under their own
heading so nobody reads them as binding.
What the reviewer actually decides on
AACE-29R-03-2.3.B.c— the controlling path changed at 1 of 1 update boundaries. §2.3.B requires each such change to be identified; whether it was legitimate is not computable from the schedule, and the report says so.SVP-2.3.B-PATH— the later file reroutes the longest path: 32 activities dropped, 1 picked up, first difference at position 0.SVP-2.2.D.1— activities present in one file and absent from the other, listed with their closest name matches on the other side, and referred with the three questions §2.2.D.1 asks (more detail, new work under a change order, or a baseline that failed to reflect the scope).
Then explain --activity ID narrates why one date is what it is, link by link —
the relation type, the lag, the calendar the lag was measured on, and the two
dates either side of it, so the reviewer can check each line against P6 without
trusting this engine. Where two predecessors impose the same bound, both are
named: a trace that picked one would invite the reply "but the other finished
the same day".
Where it breaks
- All thirty updates at once is the invocation nobody has measured. The
commands accept any number of files and the tool loads every one of them, but
the runtime is linear in the total and the largest file measured takes about 30
seconds on its own. Thirty medium updates is a coffee, not a hang; thirty large
ones has not been timed.
engine/quality/PERFORMANCE.mdhas the envelope. - There is no "just the delta" mode. Re-running the monthly submission
re-reads and re-schedules every file in the chain.
CAPABILITY-GAPS.md§5 lists a--rule IDre-run as a small item; it does not exist. - Update numbers are not read from anywhere. If the files carry no sequence
number the report says so and states that coverage of the project duration
cannot be read off numbers that are absent.
record --expected-interval DAYSis how you tell it the contract's cycle; without it the job's own median is used, which cannot detect a gap in a uniformly incomplete chain.
3. A contractor's scheduler checks their own submission
The question: what will the reviewer say, and what do I fix first?
This is the same engine pointed the other way, and until 3 September 2026 there
was no command for it: the report was ordered by verdict and severity, which is
the reviewer's question. --respond is the answer to the contractor's.
The commands
construct check MY-SUBMISSION.xer --baseline 0 --jurisdiction us_federal --respond
construct validate MY-SUBMISSION.xer --baseline 0
What comes back
The findings section unchanged — both parties read the same document, which is the only reason either can trust it — followed by Responding to this review, which is the identical set of findings grouped by what to do:
- Change in P6 before resubmitting — the blocking failures, each naming the activities by Activity ID. First, because a scheduler with two days needs the order that survives being cut short.
- Change in P6, or say why not — the failures that are not blocking.
- Inputs you can supply — the abstentions, each naming the clause parameter that would decide it. On the worked file this is 73 of 120 clauses, and it is the group a reviewer's report gives a contractor no way to see.
- Questions for the narrative, not the file — the referrals. No change to the schedule answers these; a person does.
- Nothing to do — the passes and observations, counted.
Above all of it, four sentences saying what the section is not. It does not produce a corrected schedule file, and no file this tool touched should be submitted: NAVFAC UFGS 01 32 17.00 20 makes importing schedule data through third-party software cause for rejection of the submittal. It does not state an entitlement position — whether a finding is correct, applicable, waived or worth disputing is a contractual judgment, and the tool locates findings and quotes clauses rather than arguing them.
The line is: the tool says what was found and where to find it; the person says
what it means. engine/tests/test_respond.py::test_does_not_argue asserts the
vocabulary of argument absent and there is no field through which a caller could
supply it.
Where it breaks
- The scheduler usually has the contract, and the tool has no way to know it.
Sixty of the seventy-three abstentions want a
termskey that a contractor can read off their own specification in an afternoon, and doing so is worth 25 more scored clauses on the worked file. Four annotated starting files now exist inexamples/, and a mistyped key is complained about with its near match — but there is still no command that lists the keys a given jurisdiction's rules would read, and no command that checks a supplied file against them. Writing that file remains the highest-value thing a contractor can do and the thing the tool helps with least. --respondis oncheckonly.validate, which is the other half of a self-review, has no equivalent grouping.- Nothing scores a resubmission against the previous one. "Did I fix what they asked for" is workflow 2's machinery pointed at your own files, and it works, but no command frames it that way.
4. A forensic analyst: baseline, as-built, and a delay analysis
The question: how many days, whose, and will it survive cross-examination?
The commands
construct validate BASELINE.xer ASBUILT.xer --baseline 0
construct analyse BASELINE.xer ASBUILT.xer --baseline 0 --method 3.1
construct analyse BASELINE.xer ASBUILT.xer --baseline 0 --method 3.3
construct analyse BASELINE.xer ASBUILT.xer --baseline 0 --method 3.8 --delays delays.json
construct concurrency BASELINE.xer --delays periods.json --interval 1
construct record BASELINE.xer ASBUILT.xer --baseline 0
validate first, and it is not a formality. AACE RP 29R-03 §1.5.B(1)
requires the critical path and float to come from a validated network. An
analysis on an unvalidated baseline is worthless however good the engine is, and
the source validation state is carried into every analyse report as an
assumption of that analysis.
The method is an argument, and the tool will not make it for you
--method takes a section number, or a common name only where that name means
exactly one method. TIA does not:
'TIA' could mean MIP 3.6, 3.7. Was the delay modelled against one base
schedule (3.6) or against a separate contemporaneous update per event (3.7)?
That refusal is the point of the whole package. So are these:
- MIP 3.2 has no command-line form. It segments the comparison into periods
and where the boundaries fall is the analyst's choice; §3.2 records that
variable boundaries are open to manipulation. It is reachable through
forensic.static_observational.analyse_periodic, where the boundaries and the period structure are both arguments. - MIP 3.5 has none either. §3.5 makes the modification register the deliverable and every modification must carry what changed, on whose authority, and why. None of that is in a schedule file.
- MIP 3.9 requires
--collapse-mode, never defaulted. On the same programme periodic and cumulative give five days and zero.
What the methods return, on the worked pair
- 3.1 — 93 days between the as-planned finish (2025-09-10) and the as-built
finish (2025-12-12), followed by eight limitations in the report itself,
including that the method attributes nothing to cause, is float-blind
except where
exceeds_planned_floatflags it, and supports no entitlement finding on its own. - 3.3 — 93 days of slip across 1 window, of which 0 are explained within the window and 67 are unattributed. Unattributed means computed by subtraction: float already spent, mitigation elsewhere, a delay not modelled, and a driving path that changed all leave days in it, and two schedules do not separate them. None of the four is claimed.
- 3.6 to 3.9 need
--delays, and refuse with the file shape when it is absent.impactsnames the activity by the Activity ID every finding prints, or by internaltask_id; a value that resolves to neither is refused with close matches. concurrencyreports all four doctrinal readings and, where nothing governs, picks none: "the four readings are below and they differ; which one applies is a question for the contract and the forum, and picking one here would assert an agreement between the standards that does not exist." Exit 1 when nothing governs. On the worked periods all four agreed at 9 days, and the report still says all four.
Where it breaks
- The delay events are yours, and the tool says so on the page. Neither
analysenorconcurrencyderives a delay from the schedules; both read--delaysand record that as an assumption naming every period. There is no discovery step. --delaysis hand-written JSON and there is no editor, no validator, and no worked example against a real corpus file.examples/delay-events.jsonuses"impacts": "T339", which resolves against nothing in any corpus export — it is illustrative and is written for a fixture. Getting the first one right is an hour of trial and error against the refusals, which are at least precise.concurrencywithout acritical_effectwindow is applying the functional theory to an assumption, which it states as an open question per delay: General Principle 6 requires the delay to be shown on the critical path, and the occurrence window standing in for the effect window is exactly the literal-versus-functional argument.- ANSI/ASCE/CI 67-17 defines concurrency and does not select a theory. §8.1
describes it as two or more critical delays occurring at the same time over all
or part of the delay time frame, adding that they need not share a start or
finish date; §8.3 then contemplates a delay absorbed by float before it becomes
critical, which is the functional reading of the same facts. The standard
nowhere names the literal / functional distinction, so the tool reports both
readings rather than choosing. Its own §8.3 apportionment doctrine is a third
position and is reported rather than mapped onto either. (Until 6 September the
tool said instead that this text was not in the corpus it was written from,
which was false; see
engine/quality/CONCURRENCY-GOVERNANCE.md.) - The unattributed remainder is the number the other side will attack, and the tool hands it to you labelled rather than hidden. Two schedules cannot split it; more contemporaneous updates can.
tia is a different job, and is prospective
construct tia UPDATE.xer --fragnet fragnet.json --delay-start 2024-03-01 \
--unit work_days --reviewed 0 --already-awarded
Eight steps under AACE RP 52R-06, each recording what it did, what it assumed, and whether the tool decided it or a person did. Three of its arguments are refusals in disguise and all three are right:
--reviewed Nis required in practice. Step 2 needs the last owner-reviewed update, and whether the owner reviewed one is a fact about the project record. Without it: "no schedule qualifies under AACE RP 52R-06 Step 2 for a delay beginning 2024-03-01 … Supply a reviewed update, or re-status the schedule to immediately before the delay and have it reviewed."--unitis required and never defaulted. Work days and calendar days differ by roughly forty per cent on a five-day calendar.--already-awardedunset makes Step 8 record itself as not performed, rather than as having struck nothing. Pass it bare, with no dates, to state positively that nothing was previously awarded.
--reporting-period is the one that defaults, to RP 52R-06's own example figure
of 30 days, and the report carries that as an assumption naming what it gates.
On a fortnightly contract it decides whether the analysis can proceed at all.
5. An expert being deposed reproduces a record
The question: is this the same answer, from the same files, months later?
The commands
python -m forensic.evidence record BASELINE.xer -o record.json
python -m forensic.evidence verify record.json BASELINE.xer
What comes back
WARNING REPRO.METHOD.UNCHECKED the record's method states command='evidence
record', files=['claim-01-baseline.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.
Followed, on both branches, by the sentence that matters in a deposition: 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.
Verified against the wrong file, it names every difference — the label, the file digest, the model digest, the answer digest, and each summary field with both values — and exits 1. Exit 1 is not an accusation: the commonest cause of a divergence 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.
Where it breaks
~~verify is not a construct subcommand. It is reached as
python -m forensic.evidence verify. Until 3 September 2026 it appeared in
demo/run.py and in no --help output anywhere, so a reviewer who had run
construct --help had no way to learn that the integrity claim the whole
product rests on is executable. The top-level help now names it — which tells
you it exists and does not make it a subcommand.
CAPABILITY-GAPS.md §5 item 6 calls registering it "trivial"; it has not been
done, and it was not done tonight because evidence.py was under another agent's
edit. This is the single largest discoverability defect on the surface.~~
Struck 6 September 2026: it was registered, and this page went on describing
the gap as the product's worst. construct verify is a subcommand. Measured
on this tree at engine 0.3.0, from the repository root:
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
Both exit 0, and the second prints REPRODUCED: every value this verifier
checks was recomputed from these files, followed by the notes naming what it
did not check.
It is listed in construct --help between record and the end of the command
table, cli.py registers it beside the others, and verify --help states its
own exit codes. python -m forensic.evidence verify also still works and writes
a smaller record than construct record --json does; the two are not the same
artefact, which is worth knowing before handing one to the other.
The discoverability point survives its own repair in one narrower form, and
that is what is left of the paragraph above: verify takes no --terms,
--jurisdiction or --baseline, deliberately, but --project must be given
again, because it changes every digest. A reviewer who omits it on the second
command verifies a different question from the recorded one and is told so.
Two smaller ones at the same site, both handed back rather than fixed for the same reason:
forensic.evidence's--projecthelp says the selector is "theproj_idP6 exports". It is not: the flag is passed tocli._load, which resolvesproj_short_name— the value P6 actually shows. The main CLI's--projecthelp says so correctly, so the tool contradicts itself between two help texts.forensic.evidence recordandconstruct recordare two different commands with the same name. The first writes a machine-readable reproduction record; the second writes a human-readable provenance report. Nothing in either help mentions the other.
And record is a name doing double duty in a third sense: -o is accepted
by forensic.evidence and by nothing else. Everywhere else the flag is --out.
6. What is common to all five
The flags mean one thing each, across every subcommand.
--baseline N— which of FILES is the contract baseline, 0-based, counting the order you gave them. Never guessed, and refused outright byindex, which analyses each file on its own and so has no series for an index to point into.--out PATH— write there instead of to stdout. A name ending.htmlproduces a report to open in a browser and print to PDF; anything else is Markdown. It is used for nothing else, and the confirmation that it was written goes to stderr.--json— the structured payload instead of Markdown. The same analysis, never a different one.--project ID— which project to read from a file holding several, named by theproj_short_nameP6 shows. Never guessed.--terms FILE— the contract's own values, as JSON. Never substituted from an industry norm: a rule with no term abstains and names the key. A key stated twice in one object is refused rather than read as the second value, because the file states two things and there is no correct choice between them.--jurisdiction— what governs. Never inferred from the file.--strict— also fail on a clause that could not be evaluated, not only on failures. Never on by default.
Exit codes, everywhere: 0 nothing blocking was found — which, given the
coverage figures, is a weaker claim than "conforms"; 1 the tool ran and
something is wrong or unestablished; 2 nothing was analysed, because the inputs
could not be read or an input was refused by name. Each subcommand's --help
states its own variant.
Everything that is not the report goes to stderr. Progress, refusals, and the "written to PATH" confirmation. A shell pipeline capturing the report is unaffected.
Large files say what they are doing. A file over 4MB prints one line naming it and its size before it is read — to a terminal only, never to a pipe, a redirect or a captured stderr, so it cannot reach an artefact. The largest corpus file measured takes about 30 seconds and printed nothing at all before this existed. There is no spinner and there will not be: a spinner carries its meaning entirely in an animated glyph, which is the one thing that does not survive being read aloud.
Every report opens with the same eighty-odd lines, and you should know that
before you scroll. The reservation of determination, the governing documents,
where those documents disagree, the choices that could have gone the other way,
the assumptions and the standing limitations all precede the answer — because in
a document going to a forum they belong there. Measured on the worked file, the
findings begin at line 97 of 268 for check, 90 of 124 for validate,
87 of 137 for record, 84 of 108 for index, and the driving-path trace
begins at line 105 of 235 for explain. Piped to a pager, /^## Findings
gets you there; the HTML report has a contents navigation and the Markdown does
not. This is the most common complaint a first-time user will have and it is a
deliberate trade, not an oversight.
The reports are byte-identical across runs, processes, locales, timezones and
paths. engine/tests/test_determinism.py pins it. The one field that is not is
Date prepared, which is a self-asserted local time and says so on the page.
7. Where the whole surface stops
Ordered by how often it will be hit.
- There is no command that helps you write a
terms.json. 116 of 351 rules are inert without one; 61 of those want a single value a contract states in one line. Four annotated example files landed on 3 September and are worth 25 more scored clauses on the worked file, but no command lists the keys a given jurisdiction would read. ~~and none checks a supplied file against them.~~ Corrected 2026-09-04:--termsdoes check the file it is given —cli.term_complaintsnames an unrecognised key and a key in the wrong format instead of dropping it in silence. Listing the keys a jurisdiction would read is the half that is still missing. Measured since: with all three shipped terms files supplied at once the inert count falls from 122 rules to 83, and 41 of those 83 want anasce.*oraace.review.*namespace no example file demonstrates (engine/quality/VERDICT-YIELD.md§10). This is still the largest single lever on the product. verifyis undiscoverable. §5.- A schedule with circular logic gets no conformance report. It is refused
by name with the loop in Activity IDs and an explicit statement that the
silence is not a pass — but
UFGS-071, the catalogue's only clause about circular logic, is never reached, and its 66 corpus PASSes are 66 files selected for being unable to falsify it.engine/quality/NEVER-ADVERSE.md§4 owns that finding. The honest options were to abstain the rule, or to let the rules run on an unscheduled network. ~~neither was done on 3 September 2026.~~ Superseded 2026-09-04: the first was done.UFGS-071now abstains on any snapshot carrying a CPM result — such a snapshot was necessarily loop-free before the clause was asked — so 66 unfalsifiable PASSes were withdrawn in each arm and no FAIL count moved. Handed an unscheduled snapshot the rule still decides in both directions. A schedule with circular logic still gets no conformance report, which is the part of this item that stands. - The multi-project XER needs
--projectand nothing tells you which. Naming an unknown project lists every one the file holds, which is the right refusal — but the first run is a refusal. --delaysand--fragnetare hand-written JSON with no worked example against a real file.- Thirty updates has not been timed, and the monthly resubmission loop re-reads the whole chain.
- No command answers "what changed since last month" directly. The material
is in
checkon two files and inexplain; the framing is not.
The commands, as one list
construct --version the engine version every report names
construct check FILES... conformance against the specification that governs
construct unanswered FILES... the abstentions grouped by the input that would close them
construct health FILE the DCMA 14-point screening metrics — never exits 1
construct serve a browser page on this computer, for a reviewer with no terminal
construct index FILES... many unrelated submissions, one row each
construct validate FILES... AACE 29R-03 source validation
construct explain FILE why one activity starts when it does
construct clause CLAUSE what one clause asks and what would settle it
construct analyse FILES... one named MIP
construct concurrency FILE concurrency under every doctrine
construct tia FILE prospective TIA under AACE RP 52R-06
construct record FILES... provenance, digests, and what is absent
construct verify REC FILES re-run a record's files and check the answer holds
python -m forensic.evidence record FILES... write a smaller reproduction record
python -m forensic.evidence verify REC FILES check one against the files
Corrected 6 September 2026: this list held nine of the thirteen and omitted
verify while listing its forensic.evidence form, which is the same
omission §5 above is struck for. construct with no arguments prints all
thirteen with a worked example, and that output — not this list — is the one to
trust if the two ever disagree again.
Source: docs/WORKFLOWS.md. Source commit date: 2026-09-06.