ENGINE 21
SFS-E21-LWV · LIEN WAIVERS · REV 2026-07-24 · PRODUCTION
PRODUCTION = runnable end-to-end, CI-backed, full test suite passing. All data fictional and seeded.
Lien Waiver Tracking
No lien survives the payment
A residential developer pays down a chain -- a general contractor, its subcontractors, their own lower-tier subs and material suppliers -- and every party in that chain can file a mechanics lien against the project. A lien waiver is the release that gives the lien up, and it has to march in lockstep with the money: the right kind of waiver, running through the right date, for the right amount, at every tier. This engine reads the artifacts the subcontractor payment cycle emits -- the contract register and its tier chain, the payment register, the waiver register, the lower-tier register, the exposure summary and the waiver log -- and runs twenty-five deterministic controls over them. Is every progress payment covered by a waiver that reaches its paid-through date, does every cleared payment carry an unconditional release and not merely the conditional signed at payment, did every required lower-tier party release far enough through a date re-derived from its parent's own payments, and does the unwaived exposure per project re-derive from the payments and the releases. It re-derives each tie-out where the money actually landed, compares each one with exact equality, and never writes to a source artifact.
The failures here do not look wrong while a waiver is on file, which is exactly why they survive to closing. A subcontractor signs a conditional waiver when it is paid -- conditional on the cheque clearing -- and owes an unconditional one once it has; the cheque clears, nobody chases the unconditional, and the folder looks complete because a waiver is on record while the lien right is still live. A waiver runs through a date, and a release that stops a month short of the work the payment covered leaves the last tranche unwaived with a waiver still sitting in the file. And the subcontractor's own release does not reach its rebar supplier, paid out of that same progress payment: a downstream lien survives the upstream release and attaches to the same title. Having a waiver on file is necessary and not sufficient. The engine proves the right kind of waiver reaches the right date at every tier, re-derives the unwaived exposure a title company would price the deal on, ships a planted-defect file for every registered control, and states its benchmark as a command rather than a claim.
Architecture
functional block stack · static overviewSelect a layer
Eight control families run in registry order over each portfolio file. The first proves the others had something to read and that the review date is legible, because a control that passes on absent evidence reports assurance it never performed.
Key specifications
at a glanceWhat it does for you
plain termsA residential developer pays down a tier chain -- a general contractor, its subcontractors, and their own lower-tier subs and material suppliers -- and every party in it can file a mechanics lien against the project. A lien waiver is the release, and it has to march in lockstep with the money: the right kind of waiver, running through the right date, for the right amount, at every tier. Three failures hide inside that, and none of them look wrong while a waiver is on file. A subcontractor signs a conditional waiver when it is paid, conditional on the cheque clearing, and owes an unconditional one once it has; the cheque clears, the unconditional is never chased, and the file looks complete because a waiver is on record while the lien right is still live. A waiver runs through a date, and a release that stops short of the work the payment covered leaves the last tranche unwaived, unnoticed because there is a waiver in the folder. And the subcontractor's own release does not reach its supplier, paid out of that same progress payment: a downstream lien survives the upstream release and attaches to the same title. None of these are judgment calls. They are equalities and coverage tests -- a release against the date it must reach, a waiver amount against the payment, an unwaived exposure against its re-derivation -- which a deterministic control settles better than a spreadsheet inherited by whoever holds it this year.
The seeded demo runs every control over every portfolio file7. The CLI generates 27 fictional portfolio files -- one clean baseline and one carrying each of the 26 planted defects -- then runs all 25 controls over each. The clean file returns PASS with no flags; every defect file returns REVIEW or FAIL and names the control it tripped along with the reason that control exists.
The conditional does not survive the cheque8. A test shortens the unconditional waiver on a cleared payment so it no longer reaches the paid-through date, while the conditional still does. cov_cleared_unconditional fires because a conditional release does not survive the cheque clearing, and cov_payment_covered stays silent because a release still reaches the date -- proof that a waiver on file is necessary but not sufficient.
The lower-tier release reaches the parent's paid date, to the day9. A test moves a lower-tier party's release one day either side of the date its parent was paid through. A day short, tier_waiver_covers fires because the most recent downstream work is unwaived; on the day or beyond, it stays silent -- the same one-day boundary that separates a covered supplier from a surviving downstream lien.
Functional block diagram
engineering · each block links to its sourcePlain terms
- Seeded Portfolio Files. fictional waiver-book files enter the control registry
- Structural Precondition. is the portfolio file complete and the review date legible
- Contract Register. is the register sound and does the tier chain hold
- Payment Register. is each progress payment fully and sensibly described
- Waiver Register. is each waiver well-formed and tied to a real payment for the right amount
- Coverage. is every payment covered and every cleared payment released unconditionally
- Lower Tiers. did every required lower-tier party release, far enough, through the right date
- Exposure. does the unwaived exposure per project re-derive
- Waiver Log. do the waiver-log counts reconcile to the register
- Contract, Payment & Waiver Registers. the joined authorities every coverage rule resolves against
- Verdict and Findings. every finding, with the reason it exists, ends at a person
Engineering
- Seeded Portfolio Files. generate_corpus writes one clean baseline plus 26 planted-defect files, at least one for every registered control, 27 files in total. Only the subcontracts, the payments and the lower-tier parties are stated; every waiver behind each payment, the date each party must release through, the unwaived exposure per project and the waiver-log counts are re-derived, so the relationships the engine tests are the same relationships that produced the data.
- Structural Precondition. One control. FAILs a file missing any of the six artifact types or carrying a duplicate, and FAILs an unreadable as_of, so no downstream rule holds vacuously on absent evidence.
- Contract Register. Five controls: required fields, unique sub ids, a whole tier of one or greater, a lower-tier subcontract sitting under a real parent, and a project the portfolio actually runs.
- Payment Register. Four controls: required fields, a sub that exists in the register, a positive integer-cent amount, and a paid-through date that parses and does not run past the review date.
- Waiver Register. Five controls: required fields, unique waiver ids, a known conditional-or-unconditional type, a payment that exists, and an amount that ties the payment to the cent.
- Coverage. Three controls: some release reaches each payment's paid-through date, a cleared payment carries an unconditional release through that date, and a stale conditional past the register's ageing band is flagged.
- Lower Tiers. Three controls: every required party has released, its release reaches its parent's paid-through date, and that required date re-derives from the parent's own latest payment.
- Exposure. Two controls: exposure re-derives to the cent as the payments no unconditional waiver yet stands behind, and every project carrying a payment appears in the summary.
- Waiver Log. Two controls: the total, conditional and unconditional counts tie the register, and the count of unconditionally-released payments reconciles as a review flag.
- Contract, Payment & Waiver Registers. The contract register carries each sub's id, tier, parent and project; the payment register carries each progress payment's sub, project, period, paid-through date, amount and cleared flag; the waiver register carries each release's type, through-date, amount and the payment it backs. Coverage, lower-tier and exposure are all evaluated against these three authorities, and the unwaived-exposure definition is shared so the engine and the generator cannot drift on what unwaived means.
- Verdict and Findings. Findings roll up per portfolio file: 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 carry 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 | regenerate the seeded fictional corpus, run all twenty-five controls, write both reports | per-file verdicts and every actionable finding, then the overall verdict | 2 for the bundled corpus, which carries a planted defect for every control by design | lien_report.json, lien_report.md, samples/ |
python -m lien_engine samples | analyze an existing folder of portfolio files without regenerating it | per-file verdicts and findings | 0 PASS / 1 REVIEW / 2 FAIL / 3 usage | none unless --json or --md is passed |
python -m lien_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 | 10373 passed | 0 when every test passes | none |
Benchmarks
measured demo results| Measure | Result |
|---|---|
| Engine tests10 | 10,373 tests collected live collection from the engine directory |
| Registered controls11 | 25 controls counted from the registry, not from documentation |
| Planted defects12 | 26 defect files every registered control has at least one file that trips it |
| Control coverage13 | 100 percent of controls with a planted defect no control ships without a file 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 whether a waiver book is clear to close over.
Engineering
Deterministic envelope. seeded fictional inputs, read-only operation, offline default mode.
Demo gate. FAIL on the bundled corpus, by design -- it carries a planted defect for every registered control
| Severity | Verdict | Action |
|---|---|---|
| No findings above PASS | PASS | carry the mechanically clean waiver book into the documented coverage and reporting review |
| One or more FLAG, no FAIL | REVIEW | a human resolves each flag; a conditional waiver aged past its upgrade window and a covered-payment count that does not reconcile both land here |
| One or more FAIL | FAIL | the waiver book is not treated as clear until the failing control is cleared -- a cleared payment with no unconditional release, a short or missing lower-tier waiver, or an exposure that does not re-derive |
- Read-only: portfolio files are parsed and never written back, so the engine cannot introduce the break it reports.
- Integer cents throughout, compared with exact equality; there is no tolerance band on a waiver amount or an exposure figure, and the single tolerant control reads its ageing band from the waiver register rather than inventing one.
- 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 into the number the engine is meant to audit.
- 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 missing artifact fails completeness rather than letting the controls that read it pass unread.
Operating limits
what it refuses to do- The engine never chases a waiver, releases a lien, moves a payment, or writes to a source artifact. It reports and stops.14
- It reads the six artifacts the subcontractor payment cycle emits. It does not connect to a construction-accounting system, a bank, a lien-service portal or the general ledger.15
- It is about whether a lien waiver stands behind every dollar already paid, not whether the payment should have gone out: invoice approval, three-way match and budget release gate a payment, and the accounts-payable engine owns that gate. Nothing here re-authorises a payment.16
- It proves the right kind of waiver reaches the right date for the right amount at every tier; it does not opine on whether a waiver is in the statutory form a given jurisdiction requires or whether it is properly executed. That judgment is a reviewer's.17
- All shipped data is fictional and the construction period is set in a fictional future. The corpus demonstrates the controls; it is not a record of any real developer, subcontractor, supplier, project, payment or waiver.18
See it run
control architecture
Integration
how to run itDistribution: public repository, MIT license.
- 1python -m pytest --collect-only -q -o addopts= (lien-waiver-automation/)
- 2len(lien_engine.engine.REGISTRY)
- 3distinct rule-id prefixes in lien_engine.engine.REGISTRY
- 4samples/*.json after python run.py
- 5len(lien_engine.model.DOC_TYPES)
- 6lien_engine/tests/test_control_semantics.py::test_exposure_recomputes_to_the_cent
- 7python run.py (lien-waiver-automation/); lien_engine/tests/test_planted_defects.py::test_defect_fires_its_intended_rule
- 8lien_engine/tests/test_control_semantics.py::test_a_cleared_payment_needs_an_unconditional_that_reaches_the_date
- 9lien_engine/tests/test_control_semantics.py::test_lower_tier_release_reaches_the_parents_paid_date
- 10python -m pytest --collect-only -q -o addopts= (lien-waiver-automation/)
- 11len(lien_engine.engine.REGISTRY)
- 12len(lien_engine.generate.DEFECTS)
- 13lien_engine/tests/test_planted_defects.py::test_every_registered_rule_has_a_planted_defect
- 14lien-waiver-automation/README.md and lien_engine/tests/test_readonly_and_determinism.py::test_analysis_does_not_touch_the_source
- 15lien_engine/model.py DOC_TYPES
- 16lien_engine/engine.py module docstring (Scope)
- 17lien_engine/engine.py check_cov_cleared_unconditional
- 18lien_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