Skip to main content Skills Marketplace 커뮤니티가 만든 AI 스킬을 발견하고 탐색하세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction --skill lien-waiver-tracker명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Zip 다운로드 다운로드 중... name lien-waiver-tracker description Track and manage construction lien waivers. Monitor conditional and unconditional waivers, ensure compliance before payments, and prevent lien exposure. homepage https://datadrivenconstruction.io metadata {"openclaw":{"emoji":"📝","os":["darwin","linux","win32"],"homepage":"https://datadrivenconstruction.io","requires":{"bins":["python3"]}}}
Lien Waiver Tracker
Overview
Track and manage lien waivers throughout the construction payment process. Ensure proper waivers are received before releasing payments, monitor waiver status by subcontractor, and minimize lien exposure.
Lien Waiver Types
┌─────────────────────────────────────────────────────────────────┐
│ LIEN WAIVER TYPES │
├─────────────────────────────────────────────────────────────────┤
│ │
│ CONDITIONAL UNCONDITIONAL │
│ ─────────── ───────────── │
│ 📋 Progress - Conditional ✅ Progress - Unconditional │
│ Effective when paid Immediately effective │
│ Use with payment Use after check clears │
│ │
│ 📋 Final - Conditional ✅ Final - Unconditional │
│ For final payment For final payment │
│ Upon receipt of funds After funds received │
│ │
└─────────────────────────────────────────────────────────────────┘
Technical Implementation
from dataclasses import dataclass, field
from typing import List , Dict , Optional
from datetime import datetime, timedelta
from enum import Enum
class WaiverType (Enum ):
CONDITIONAL_PROGRESS = "conditional_progress"
UNCONDITIONAL_PROGRESS = "unconditional_progress"
CONDITIONAL_FINAL = "conditional_final"
UNCONDITIONAL_FINAL = "unconditional_final"
class WaiverStatus (Enum ):
REQUESTED = "requested"
RECEIVED = "received"
VERIFIED = "verified"
REJECTED = "rejected"
MISSING = "missing"
class PaymentStatus (Enum ):
PENDING = "pending"
APPROVED = "approved"
HELD = "held"
RELEASED = "released"
@dataclass
class Subcontractor :
id : str
name: str
trade: str
contract_amount: float
contact_name: str
contact_email: str
tier: int = 1
@dataclass
:
:
subcontractor_id:
waiver_type: WaiverType
payment_application:
through_date: datetime
amount:
status: WaiverStatus = WaiverStatus.REQUESTED
requested_date: datetime = field(default_factory=datetime.now)
received_date: [datetime] =
verified_by: =
file_path: =
notes: =
:
number:
period_end: datetime
subcontractor_id:
amount_requested:
amount_approved:
retainage:
status: PaymentStatus = PaymentStatus.PENDING
waivers_complete: =
payment_date: [datetime] =
:
subcontractor_id:
subcontractor_name:
total_paid:
unconditional_waivers:
conditional_pending:
exposure:
:
( ):
.project_id = project_id
.project_name = project_name
.subcontractors: [ , Subcontractor] = {}
.waivers: [ , LienWaiver] = {}
.pay_apps: [ , PaymentApplication] = {}
( ) -> Subcontractor:
sub = Subcontractor(
= ,
name=name,
trade=trade,
contract_amount=contract_amount,
contact_name=contact_name,
contact_email=contact_email,
tier=tier
)
.subcontractors[ ] = sub
sub
( ) -> PaymentApplication:
subcontractor_id .subcontractors:
ValueError( )
retainage = amount_requested * retainage_rate
amount_approved = amount_requested - retainage
pay_app = PaymentApplication(
number=number,
period_end=period_end,
subcontractor_id=subcontractor_id,
amount_requested=amount_requested,
amount_approved=amount_approved,
retainage=retainage
)
key =
.pay_apps[key] = pay_app
.request_waiver(subcontractor_id, number, period_end, amount_approved)
pay_app
( ) -> LienWaiver:
waiver_id =
waiver = LienWaiver(
=waiver_id,
subcontractor_id=subcontractor_id,
waiver_type=waiver_type,
payment_application=pay_app_number,
through_date=through_date,
amount=amount
)
.waivers[waiver_id] = waiver
waiver
( ) -> LienWaiver:
waiver_id .waivers:
ValueError( )
waiver = .waivers[waiver_id]
waiver.status = WaiverStatus.RECEIVED
waiver.received_date = datetime.now()
waiver.file_path = file_path
waiver.verified_by = verified_by
._check_pay_app_waivers(waiver.subcontractor_id, waiver.payment_application)
waiver
( ) -> LienWaiver:
waiver_id .waivers:
ValueError( )
waiver = .waivers[waiver_id]
waiver.status = WaiverStatus.VERIFIED
waiver.verified_by = verified_by
waiver
( ) -> LienWaiver:
waiver_id .waivers:
ValueError( )
waiver = .waivers[waiver_id]
waiver.status = WaiverStatus.REJECTED
waiver.notes =
waiver
( ) -> LienWaiver:
waiver_id .waivers:
ValueError( )
waiver = .waivers[waiver_id]
new_type = (WaiverType.UNCONDITIONAL_PROGRESS
waiver.waiver_type == WaiverType.CONDITIONAL_PROGRESS
WaiverType.UNCONDITIONAL_FINAL)
.request_waiver(
waiver.subcontractor_id,
waiver.payment_application,
waiver.through_date,
waiver.amount,
new_type
)
( ):
key =
key .pay_apps:
pay_app = .pay_apps[key]
related_waivers = [
w w .waivers.values()
w.subcontractor_id == subcontractor_id
w.payment_application == pay_app_number
]
pay_app.waivers_complete = (
w.status [WaiverStatus.RECEIVED, WaiverStatus.VERIFIED]
w related_waivers
)
( ) -> LienExposure:
subcontractor_id .subcontractors:
ValueError( )
sub = .subcontractors[subcontractor_id]
total_paid = (
pa.amount_approved pa .pay_apps.values()
pa.subcontractor_id == subcontractor_id
pa.status == PaymentStatus.RELEASED
)
unconditional = (
w.amount w .waivers.values()
w.subcontractor_id == subcontractor_id
w.waiver_type [WaiverType.UNCONDITIONAL_PROGRESS, WaiverType.UNCONDITIONAL_FINAL]
w.status [WaiverStatus.RECEIVED, WaiverStatus.VERIFIED]
)
conditional = (
w.amount w .waivers.values()
w.subcontractor_id == subcontractor_id
w.waiver_type [WaiverType.CONDITIONAL_PROGRESS, WaiverType.CONDITIONAL_FINAL]
w.status [WaiverStatus.RECEIVED, WaiverStatus.VERIFIED]
)
exposure = total_paid - unconditional
LienExposure(
subcontractor_id=subcontractor_id,
subcontractor_name=sub.name,
total_paid=total_paid,
unconditional_waivers=unconditional,
conditional_pending=conditional,
exposure=exposure
)
( ) -> [ ]:
missing = []
waiver .waivers.values():
waiver.status [WaiverStatus.REQUESTED, WaiverStatus.MISSING]:
sub = .subcontractors.get(waiver.subcontractor_id)
missing.append({
: waiver. ,
: sub.name sub waiver.subcontractor_id,
: waiver.payment_application,
: waiver.amount,
: waiver.waiver_type.value,
: waiver.requested_date,
: (datetime.now() - waiver.requested_date).days
})
(missing, key= x: -x[ ])
( ) -> :
subcontractor_id .subcontractors:
ValueError( )
sub = .subcontractors[subcontractor_id]
waivers = [w w .waivers.values() w.subcontractor_id == subcontractor_id]
by_status = {}
w waivers:
s = w.status.value
by_status[s] = by_status.get(s, ) +
{
: sub.name,
: (waivers),
: by_status,
: (w.amount w waivers),
: (w.amount w waivers w.status == WaiverStatus.VERIFIED)
}
( ) -> :
key =
key .pay_apps:
{ : , : }
pay_app = .pay_apps[key]
waivers = [
w w .waivers.values()
w.subcontractor_id == subcontractor_id
w.payment_application == pay_app_number
w.waiver_type == WaiverType.CONDITIONAL_PROGRESS
]
waivers:
{ : , : }
w waivers:
w.status [WaiverStatus.RECEIVED, WaiverStatus.VERIFIED]:
{ : , : }
{ : , : , : pay_app.amount_approved}
( ) -> :
lines = [
,
,
,
,
,
,
,
,
,
,
,
,
,
,
]
total_exposure =
sub_id .subcontractors:
exposure = .calculate_exposure(sub_id)
total_exposure += exposure.exposure
lines.append(
)
lines.extend([
,
,
,
])
missing = .get_missing_waivers()
missing:
lines.append( )
lines.append( )
m missing[: ]:
lines.append(
)
:
lines.append( )
.join(lines)
Quick Start
tracker = LienWaiverTracker("PRJ-001" , "Office Tower" )
tracker.add_subcontractor(
"SUB-001" , "ABC Mechanical" , "HVAC" ,
contract_amount=500000 ,
contact_name="John Smith" ,
contact_email="john@abcmech.com"
)
tracker.add_subcontractor(
"SUB-002" , "XYZ Electric" , "Electrical" ,
contract_amount=350000 ,
contact_name="Jane Doe" ,
contact_email="jane@xyzelectric.com"
)
pa1 = tracker.create_payment_application(
number=1 ,
period_end=datetime.now(),
subcontractor_id="SUB-001" ,
amount_requested=50000
)
waiver_id = f"LW-SUB-001-1"
tracker.receive_waiver(waiver_id, "/waivers/sub001_pa1.pdf" , "PM" )
result = tracker.can_release_payment("SUB-001" , 1 )
print (f"Can release: {result['can_release' ]} - {result['reason' ]} " )
exposure = tracker.calculate_exposure("SUB-001" )
print (f"Lien exposure: ${exposure.exposure:,.2 f} " )
print (tracker.generate_report())
Requirements
pip install (no external dependencies)
class
LienWaiver
id
str
str
int
float
Optional
None
str
""
str
""
str
""
@dataclass
class
PaymentApplication
int
str
float
float
float
bool
False
Optional
None
@dataclass
class
LienExposure
str
str
float
float
float
float
class
LienWaiverTracker
"""Track and manage construction lien waivers."""
def
__init__
self, project_id: str , project_name: str
self
self
self
Dict
str
self
Dict
str
self
Dict
str
def
add_subcontractor
self, id : str , name: str , trade: str ,
contract_amount: float , contact_name: str ,
contact_email: str , tier: int = 1
"""Add subcontractor to tracking."""
id
id
self
id
return
def
create_payment_application
self, number: int , period_end: datetime,
subcontractor_id: str , amount_requested: float ,
retainage_rate: float = 0.10
"""Create payment application record."""
if
not
in
self
raise
f"Subcontractor {subcontractor_id} not found"
f"{subcontractor_id} -{number} "
self
self
return
def
request_waiver
self, subcontractor_id: str , pay_app_number: int ,
through_date: datetime, amount: float ,
waiver_type: WaiverType = WaiverType.CONDITIONAL_PROGRESS
"""Request lien waiver from subcontractor."""
f"LW-{subcontractor_id} -{pay_app_number} "
id
self
return
def
receive_waiver
self, waiver_id: str , file_path: str ,
verified_by: str = ""
"""Record receipt of lien waiver."""
if
not
in
self
raise
f"Waiver {waiver_id} not found"
self
self
return
def
verify_waiver
self, waiver_id: str , verified_by: str
"""Verify waiver details are correct."""
if
not
in
self
raise
f"Waiver {waiver_id} not found"
self
return
def
reject_waiver
self, waiver_id: str , reason: str
"""Reject waiver (incorrect amount, wrong form, etc.)."""
if
not
in
self
raise
f"Waiver {waiver_id} not found"
self
f"Rejected: {reason} "
return
def
convert_to_unconditional
self, waiver_id: str , payment_date: datetime
"""Convert conditional waiver to unconditional after payment clears."""
if
not
in
self
raise
f"Waiver {waiver_id} not found"
self
if
else
return
self
def
_check_pay_app_waivers
self, subcontractor_id: str , pay_app_number: int
"""Check if all waivers for pay app are received."""
f"{subcontractor_id} -{pay_app_number} "
if
not
in
self
return
self
for
in
self
if
and
all
in
for
in
def
calculate_exposure
self, subcontractor_id: str
"""Calculate lien exposure for subcontractor."""
if
not
in
self
raise
f"Subcontractor {subcontractor_id} not found"
self
sum
for
in
self
if
and
sum
for
in
self
if
and
in
and
in
sum
for
in
self
if
and
in
and
in
return
def
get_missing_waivers
self
List
Dict
"""Get list of missing or pending waivers."""
for
in
self
if
in
self
"waiver_id"
id
"subcontractor"
if
else
"pay_app"
"amount"
"type"
"requested_date"
"days_outstanding"
return
sorted
lambda
"days_outstanding"
def
get_waiver_status_by_sub
self, subcontractor_id: str
Dict
"""Get waiver status summary for subcontractor."""
if
not
in
self
raise
f"Subcontractor {subcontractor_id} not found"
self
for
in
self
if
for
in
0
1
return
"subcontractor"
"total_waivers"
len
"by_status"
"total_amount"
sum
for
in
"verified_amount"
sum
for
in
if
def
can_release_payment
self, subcontractor_id: str , pay_app_number: int
Dict
"""Check if payment can be released."""
f"{subcontractor_id} -{pay_app_number} "
if
not
in
self
return
"can_release"
False
"reason"
"Payment application not found"
self
for
in
self
if
and
and
if
not
return
"can_release"
False
"reason"
"No conditional waiver received"
for
in
if
not
in
return
"can_release"
False
"reason"
f"Waiver {w.id } not verified"
return
"can_release"
True
"reason"
"All waivers verified"
"amount"
def
generate_report
self
str
"""Generate lien waiver status report."""
"# Lien Waiver Status Report"
""
f"**Project:** {self.project_name} "
f"**Date:** {datetime.now().strftime('%Y-%m-%d' )} "
""
"## Summary"
""
f"- Total Subcontractors: {len (self.subcontractors)} "
f"- Total Waivers Tracked: {len (self.waivers)} "
f"- Missing Waivers: {len (self.get_missing_waivers())} "
""
"## Exposure by Subcontractor"
""
"| Subcontractor | Paid | Unconditional | Exposure |"
"|---------------|------|---------------|----------|"
0
for
in
self
self
f"| {exposure.subcontractor_name} | ${exposure.total_paid:,.0 f} | "
f"${exposure.unconditional_waivers:,.0 f} | ${exposure.exposure:,.0 f} |"
f"| **TOTAL** | | | **${total_exposure:,.0 f} ** |"
""
"## Missing Waivers"
""
self
if
"| Subcontractor | Pay App | Amount | Days Outstanding |"
"|---------------|---------|--------|------------------|"
for
in
10
f"| {m['subcontractor' ]} | #{m['pay_app' ]} | "
f"${m['amount' ]:,.0 f} | {m['days_outstanding' ]} |"
else
"*No missing waivers*"
return
"\n"