Measured, not asserted
Published
Before an owner releases a progress payment, everyone who could file a lien has to have signed it away. Those parties are found by walking down the contract tiers, and a walk that cannot read the tier links does not fail -- it finishes immediately, having correctly cleared everybody it found. This test renamed that link field, and $344,750.00 went out against zero examined claimants while the owner's completeness rule passed and printed a sentence that was entirely true.
The parties who can lien you are the ones you have to go looking for
Paying the general contractor does not clear an owner's title. A concrete sub, a rebar placer working under that sub, a glazing supplier two tiers down -- each of them can record a lien against the property for work they were not paid for, whether or not the owner has ever heard of them. So the release process walks down the contract tiers, collects everyone who filed a preliminary notice, and checks that each has furnished a waiver covering the amount and the period being paid.
The fixture is application 14 on the same project as the progress-billing article: $344,750.00, period ending day 421. Below the general contractor sit three first-tier subcontractors and two second-tier parties. Four of the five have furnished unconditional waivers. The glazing supplier, two tiers down and owed $47,250.00, has furnished a conditional one -- a real waiver, correctly executed, that releases nothing until it is actually paid. So the correct answer is that the payment is withheld pending a joint check.
The release engine is copied in unprotected. Its defaults are the incurious ones: walk one tier, accept any waiver on file.
Protection on its own moved nothing
Five areas, five presets, twenty-five comparisons, every protected output byte-identical to the unprotected run, under a guard that runs each original twice before comparing anything to it.
Everything below required member renaming aimed at a name the installed release engine also reads.
Losing the links empties the walk, and the completeness rule approves of it
Renaming the field that links each party to its lower tiers produced tiers-walked=1 claimants-found=0 cleared=0[] blocked=0 frontier-open=false => FUNDS RELEASED TO THE CONTRACTOR, and the owner's release rule passed.
That rule is a good one and it is the mitigation this series has recommended for several passes: refuse a release that ran out of tiers with parties still unexamined. It could not help here. A graph whose link field cannot be read is exhausted at depth zero -- the frontier really is empty, no branch really was skipped, and the rule's sentence is true. It examined every claimant it could see. There were none.
$344,750.00 left the owner's account with a conditional waiver outstanding at the second tier, and the record of the decision says the check was performed and satisfied.
Losing the depth instead is caught cold
Renaming the option that says how many tiers to walk reverts it to the library's default of one, and this time the rule refused: release-rejected(release-check(the tier walk stopped with lower-tier parties unexamined)).
The difference between the two is the whole lesson. A depth limit leaves a visible edge -- parties were found, and more were known to exist beyond them. A missing link field leaves nothing at all, and an assertion defined over the traversal's own state can only see work it knows was left undone. The evidence the check reads was produced by the same lost name.
Renaming the depth together with the flag that enables the rule shows the other half of it: three claimants found, frontier-open=true computed, printed, and consulted by nothing, funds released. Renaming the link field together with that same flag releases the money with frontier-open=false -- which is not the same shape, because a rule that was still running would have passed it anyway.
The requirement and the record it is compared against fail in opposite directions
The release requires an unconditional waiver. That requirement lives in two places: an option naming the kind required, and a field on each waiver naming the kind furnished.
Renaming the option reverts it to the library's default of "any waiver on file will do", and the conditional waiver is accepted: cleared=5 blocked=0 => FUNDS RELEASED TO THE CONTRACTOR. Renaming the field on the waiver records instead makes every waiver fail its comparison, and all five claimants block: cleared=0 blocked=5 => PAYMENT WITHHELD PENDING JOINT CHECK. The same concept, one name on each side of the boundary, failing open and closed respectively.
Renaming both together is identical to renaming the option alone -- funds released. The permissive half is consulted first, so the half that would have screamed is switched off by the half that fails quietly. That is a pattern this series has now measured in many domains, and it is the reason a combined arm reading clean is not evidence that its halves are clean.
The arm where the verdict survives and the payee does not
Renaming the claimant identifier left the decision entirely correct -- the conditional waiver still blocked, the payment still withheld -- and produced cleared=4[ ] blocked=1[undefined(waiver is conditional, the release requires unconditional)].
The reason for withholding is intact and legible. The party it concerns has no name. A joint check is an instrument made out to two payees, and this output cannot tell you who the second one is. The right answer, about nobody in particular.
The quantities, as everywhere else in this pass, failed loudly: renaming the payment amount or the rule's own permission flag refused or rejected outright. Losing a value fails closed; losing what a value means fails open.
What to do about it
Reconcile against something outside the walk. The number of parties who filed preliminary notices is known to the title company, and it is written on their side of the boundary. Compare the claimants your traversal found against that count and an empty walk becomes an immediate, unmissable failure instead of a clean release. An assertion that reasons only about the traversal's own state cannot distinguish a complete answer from an empty one.
Treat an empty claimant list as an event requiring a person. On a project with five preliminary notices on file, a release that examined nobody is not a tidy result.
Make a party with no readable identity a refusal. A waiver you cannot attribute is not a waiver, and a joint check you cannot address is not a remedy. This is the same discipline as the access-decision logging question in regulated web applications: a decision record that cannot name its subject has not recorded the decision.
And assert the outcome. Release one known application end to end and assert the number of claimants examined, the number cleared, the number blocked, and their identifiers. That fails in every arm in this article. A test that checks the waiver requirement is configured passes in most of them.
Frequently asked questions
Did obfuscation change any payment release on its own?
No. Five areas ran through five presets with every protected output byte-identical to the unprotected run. Every failure here required member renaming aimed at a name the installed release engine also reads.
What happened when the tier link field was renamed?
The walk was exhausted at depth zero. Zero claimants were found, zero were blocked, and $344,750.00 was released with a conditional waiver outstanding two tiers down -- while the owner's completeness rule passed and truthfully reported that no tier had been left open.
Why did the completeness rule not catch it?
Because it is defined over the traversal's own state. It refuses a walk that ran out of tiers with parties still unexamined. Here there were no parties to leave unexamined, so nothing was left undone. The evidence the check reads was produced by the same lost name.
Was anything caught?
Yes. Renaming the option that says how many tiers to walk was refused outright, because that leaves a visible edge -- parties were found and more were known to exist beyond them. A depth limit looks like stopping short; a missing link does not.
What is the difference between the two waiver-kind arms?
The option naming the required kind reverts to 'any waiver will do' and the conditional waiver is accepted, so the money goes out. The field naming the kind furnished makes every comparison fail, so all five claimants block and the payment is withheld. The same concept fails open on one side of the boundary and closed on the other.
Did renaming both halves together help?
No, and this is the trap. The union is identical to the permissive half alone -- funds released -- because the permissive half is consulted first. A combined arm reading clean is not evidence that its halves are clean.
What is the cheapest test that would have caught this?
Release one known application end to end and assert how many claimants were examined, cleared and blocked, by identifier, and reconcile that count against the title company's register of preliminary notices. That fails in every arm here.
Related reading