Initial commit: Conflict Checker

Cross-discipline design-contradiction checker for construction drawing
sets. Standalone tool broken out from Iron_Bid; a pipeline stage may
later fold back into Iron_Bid.

Pipeline: PDF->images -> per-sheet assertion extraction -> deterministic
clustering by location -> per-cluster reasoning -> report.
Includes CLI (cli/run_check.py) and web UI (backend/main.py).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-03 00:22:02 +00:00
co-authored by Claude Opus 4.8
commit 1d248a8808
262 changed files with 8209 additions and 0 deletions
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""
run_check.py - CLI entry point for the conflict checker (the tuning loop).
Usage:
python cli/run_check.py path/to/set.pdf [--out out_dir]
Run from the project root so `backend` is importable.
"""
import os
import sys
import argparse
# Make the project root importable when run as a script.
_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if _ROOT not in sys.path:
sys.path.insert(0, _ROOT)
from backend.pipeline.runner import run_pipeline # noqa: E402
def main() -> int:
parser = argparse.ArgumentParser(description="Cross-discipline drawing conflict checker")
parser.add_argument("pdf", help="Path to the PDF drawing set")
parser.add_argument("--out", default=None,
help="Directory for artifacts (default: out/<pdf-stem>)")
parser.add_argument("--project-name", default=None)
parser.add_argument("--address", default=None)
parser.add_argument("--occupancy", default=None)
parser.add_argument("--work-type", default=None,
help="new_building | remodel | tenant_improvement | addition | ...")
args = parser.parse_args()
if not os.path.isfile(args.pdf):
print(f"Error: file not found: {args.pdf}")
return 1
project_input = {
k: v for k, v in {
"project_name": args.project_name, "address": args.address,
"occupancy": args.occupancy, "work_type": args.work_type,
}.items() if v
}
out_dir = args.out or os.path.join("out", os.path.splitext(os.path.basename(args.pdf))[0])
report = run_pipeline(args.pdf, out_dir=out_dir, project_input=project_input or None)
s = report["summary"]
print("\n" + "=" * 60)
print(f" Conflicts found: {s['conflicts_found']} "
f"(high {s['by_severity']['high']}, "
f"medium {s['by_severity']['medium']}, "
f"low {s['by_severity']['low']})")
print(f" Report: {os.path.join(out_dir, 'report.md')}")
print("=" * 60)
return 0
if __name__ == "__main__":
raise SystemExit(main())