ENGINE 11
SFS-E11-DRW · DRAW CONTROLS · REV 2026-07-22 · PRODUCTION
PRODUCTION = runnable end-to-end, CI-backed, full test suite passing. All data fictional and seeded.
Project Draw
The identity a draw has to prove
A construction loan draw is the moment a developer asks a lender for money, and it is almost entirely arithmetic. This engine reads the artifacts the draw cycle already emits and runs thirty-four deterministic controls over them: do cumulative draws equal cumulative costs, does the form the lender receives agree with the working paper behind it, was contingency drawn only as fast as the work was earned, and were the costs actually incurred inside the period being billed. It never bills, never funds, and never writes to a source artifact. Every exception it raises ends at a person.
The failure modes here are not exotic, which is exactly why they survive review: a retention release moved in one schedule but not the other, a contingency line drawn faster than the work it covers, a cost posted two days after cutoff and billed anyway. Each is invisible in a spreadsheet and obvious to a rule. The engine ships one planted defect per registered control, so the benchmark is a command rather than a claim.
Architecture
functional block stack · static overviewSelect a layer
Eight control families run in registry order over each draw package. The first proves the others had something to read.
Key specifications
at a glanceWhat it does for you
plain termsThree draw failures recur across development accounting and all three survive review, because each one foots on the page the preparer is looking at. Retention released in the reconciliation but never moved into the current period leaves the schedule internally consistent and the identity broken. Contingency drawn ahead of percent complete looks like an ordinary line item until the final draw arrives with nothing left to fund the work that remains. A cost posted after cutoff and billed anyway creates a reconciliation item that surfaces a month later, by which time the draw it belongs to has already funded. None of the three is exotic; all three are arithmetic, and arithmetic is exactly what a deterministic control asserts better than a reviewer under a deadline.
The seeded demo runs every control over every package7. The CLI generates 37 fictional draw packages -- one clean baseline and one carrying each planted defect -- then runs all 34 controls over each. The clean package returns PASS with no flags; every defect package returns REVIEW or FAIL and names the control it tripped along with the reason that control exists.
One cent of drift fails the reconciliation8. The identity is asserted with integer-cent equality and no tolerance band. Tests move the reconciliation by a single cent in each direction and assert the control fails both times, then restore it and assert the control holds. A control that tolerates a penny has not proved the draw ties.
Contingency cannot authorise itself9. Percent complete is measured on the productive lines of a cost class, excluding the contingency line. A test pushes contingency exactly to its ceiling and asserts it passes, then one cent past and asserts it fails -- which would not hold if the contingency line's own spend inflated the progress that authorises it.
Functional block diagram
engineering · each block links to its sourcePlain terms
- Seeded Draw Packages. fictional draw packages enter the control registry
- Structural Precondition. is the package complete enough to be worth reading
- Reconciliation Identity. do draws to date equal costs to date
- Lender Form Agreement. does the lender's form match its working paper
- Contingency Discipline. was contingency drawn only as fast as work was earned
- Cycle and Cutoff. were these costs incurred inside the period billed
- Accrual Policy. were accruals within policy and reversed
- Supporting Documentation. is the package signed, backed up and distributed
- Funding and Follow-up. did the money arrive, and was a late draw chased
- Verdict and Findings. every finding, with the reason it exists, ends at a person
Engineering
- Seeded Draw Packages. generate_corpus writes one clean baseline plus one planted defect per registered control, 37 packages in total. The baseline is derived rather than typed -- line disbursements are chosen and every dependent figure is computed from them -- so a planted defect is guaranteed to be the only thing wrong.
- Structural Precondition. Two controls. Fails a package missing any of the six artifact types, so no downstream rule can hold vacuously.
- Reconciliation Identity. Eight controls. C minus D must be exactly zero; the rest localise the break.
- Lender Form Agreement. Eight controls covering cross-foot, derivation, column footing and three cross-artifact ties.
- Contingency Discipline. Two controls, governed per cost class, with the contingency line excluded from its own denominator.
- Cycle and Cutoff. Five controls over accounting dates, posting dates, approval SLAs and milestone ordering.
- Accrual Policy. Two controls: materiality with an immediate-payment exemption, and prior-period reversal.
- Supporting Documentation. Four controls over signature authority, invoice backup coverage, schedule of values and distribution.
- Funding and Follow-up. Three controls over the agreement's funding window, follow-up evidence and the working-capital floor.
- Verdict and Findings. Findings roll up per package: any FAIL is FAIL, FLAGs without FAILs are REVIEW, clean is PASS. The CLI exit code is the verdict, so a pipeline can gate on it. Reports are byte-stable, carrying no timestamps or absolute paths, which is what makes the committed report diffable.
Instruction set
every public command| Command | Operation | Output | Exit | Artifacts |
|---|---|---|---|---|
python run.py | generate the seeded fictional corpus, run all thirty-four controls, write both reports | per-package verdicts and every actionable finding, then the overall verdict | 2 for the bundled corpus, which carries one planted defect per control by design | draw_report.md, draw_report.json, samples/ |
python -m draw_engine samples | analyze an existing folder of draw packages without regenerating it | per-package verdicts and findings | 0 PASS / 1 REVIEW / 2 FAIL / 3 usage | none unless --json or --md is passed |
python -m draw_engine samples --generate --quiet | regenerate the corpus and print only the overall verdict | a single verdict line | 0 PASS / 1 REVIEW / 2 FAIL / 3 usage | samples/ |
python -m pytest | run the full test suite for this engine | 10392 passed | 0 when every test passes | none |
Benchmarks
measured demo results| Measure | Result |
|---|---|
| Engine tests10 | 10,392 tests collected live collection from the engine directory |
| Registered controls11 | 34 controls counted from the registry, not from documentation |
| Planted defects12 | 36 defect packages every registered control has at least one package that trips it |
| Control coverage13 | 100 percent of controls with a planted defect no control ships without a package demonstrating it firing |
Control characteristics
engineeringPlain terms
The engine has no write path and therefore no autonomy to gate. It produces findings; a person decides.
Engineering
Deterministic envelope. seeded fictional inputs, read-only operation, offline default mode.
Demo gate. FAIL on the bundled corpus, by design -- it carries one planted defect per registered control
| Severity | Verdict | Action |
|---|---|---|
| No findings above PASS | PASS | send the mechanically clean package to the documented manual review |
| One or more FLAG, no FAIL | REVIEW | a human resolves each flag before submittal; none of them blocks arithmetically |
| One or more FAIL | FAIL | the package does not go to the lender until the failing control is cleared |
- Read-only: packages are parsed and never written back, so the engine cannot introduce the defect it reports.
- Integer cents throughout, compared with exact equality; there is no tolerance band anywhere in the registry.
- A value that should be integer cents but is not produces an AMOUNT_INVALID finding contained to the one row it was read on, rather than being coerced.
- Deterministic and byte-stable: same inputs produce the same findings in the same order, with no timestamps or absolute paths in any output.
- Absent evidence is never a passing control; a package missing an artifact fails the structural precondition before any other rule runs.
Operating limits
what it refuses to do- The engine never bills a lender, funds a draw, posts a cost or writes to a source artifact. It reports and stops.14
- It reads the six fixed-format artifacts a draw cycle emits. It does not connect to a lender portal, an accounting ledger or a bank.15
- It cannot judge whether a declared variance is adequately explained -- only that an explanation is present when a variance is. That judgment is a reviewer's.16
- Lender-specific requirements vary by loan agreement. The engine checks the requirements declared in the package, not the agreement itself.17
- All shipped data is fictional. The corpus demonstrates the controls; it is not a record of any real project, lender or draw.18
See it run
brand animation
Integration
how to run itDistribution: public repository, MIT license.
- 1python -m pytest --collect-only -q -o addopts= (project-draw-automation/)
- 2len(draw_engine.engine.REGISTRY)
- 3distinct rule-id prefixes in draw_engine.engine.REGISTRY
- 4samples/*.json after python run.py
- 5len(draw_engine.model.DOC_TYPES)
- 6draw_engine/tests/test_control_semantics.py
- 7python run.py (project-draw-automation/); output re-run on demand
- 8draw_engine/tests/test_control_semantics.py::test_recon_identity_has_no_tolerance
- 9draw_engine/tests/test_control_semantics.py::test_contingency_ceiling_excludes_itself_from_progress
- 10python -m pytest --collect-only -q -o addopts= (project-draw-automation/)
- 11len(draw_engine.engine.REGISTRY)
- 12len(draw_engine.generate.DEFECTS)
- 13draw_engine/tests/test_planted_defects.py::test_every_registered_rule_has_a_planted_defect
- 14project-draw-automation/README.md and draw_engine/tests/test_readonly_and_determinism.py
- 15draw_engine/model.py DOC_TYPES
- 16draw_engine/engine.py recon_variance_explained
- 17draw_engine/engine.py doc_schedule_of_values and fund_within_agreement_days
- 18draw_engine/generate.py
Show us where the hours go.
One conversation: you describe the work that consumes your team's month; we tell you plainly what this engine can take over, what it can't, and what a scoped first phase would cost. Your people keep approval authority.
Book a free consultation