woogiandCursor bf508bfdf6
Docker Release / build-and-push (push) Successful in 57s
Docker Release / release (push) Skipped
Expand session notes with API, job log, and model selection details.
Keep NOTES.md current for new sessions after the job-log and dual-model UI work.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-31 20:21:35 -05:00
2026-07-03 00:22:02 +00:00
2026-07-03 00:22:02 +00:00
2026-07-03 00:22:02 +00:00
2026-07-03 00:22:02 +00:00
2026-07-03 00:22:02 +00:00

Conflict Checker

Cross-discipline design contradiction detector for construction drawing sets. Built for architects to back-check a set (Arch / Struct / Mech / Elec / Plumb / FP / …) before it goes out to a GC for bid. It flags information that disagrees between disciplines — e.g. the RCP ceiling height vs the mechanical duct elevation, a door schedule count vs the floor plan, a column that lands in a corridor.

This is not the IronBid scope-ownership conflict checker (who-owns-what for bidding). That stays in IronBid; this tool is a separate, design-QA pipeline.

How it works (hybrid extract-then-compare)

PDF  ->  page images  ->  per-sheet grounded facts  ->  cluster by location  ->  reason  ->  report
        Stage 0           Stage 1 (vision)             Stage 2 (no LLM)        Stage 3      Stage 4
  1. Stage 0 pdf_processor — PDF pages to base64 JPEGs (100 DPI, 2400px cap).
  2. Stage 1 extractor — one vision call per sheet extracts discrete, grounded assertions (each carries verbatim source_text and a location_key). A grounding guard drops any assertion whose number isn't in its own source text.
  3. Stage 2 clusterer — deterministic: groups assertions that refer to the same grid / room / tag across ≥2 disciplines (or a schedule-vs-plan within one). This keeps Stage 3 calls small and few.
  4. Stage 3 conflict_checker — one reasoning call per cluster (with the relevant sheet images) decides whether the disciplines genuinely contradict, and classifies it.
  5. Stage 4 reportconflicts.json + a readable report.md.

The model is google/gemini-2.5-pro via OpenRouter (configurable). All prompt tuning lives in backend/prompts.py — the system prompts, the attribute vocabulary, and the conflict taxonomy.

Setup

cp backend/.env.example backend/.env   # set AI_API_KEY (and SMTP if you want email)
docker compose up -d --build
# -> http://localhost:8099

Check health: curl http://localhost:8099/health

Logs: docker compose logs -f app

CLI inside the container (mount your PDF read-only):

docker compose run --rm \
  -v "$(pwd)/samples/your_set.pdf:/data/set.pdf:ro" \
  app python cli/run_check.py /data/set.pdf --out /app/backend/outputs/cli-run

Persistent data lives in Docker volumes (uploads, outputs, llm_cache). To bind mount host directories instead, replace the named volumes in docker-compose.yml.

For hybrid mode (local vLLM on the host), set LOCAL_BASE_URL=http://host.docker.internal:8000/v1 in backend/.env. Compose already maps host.docker.internal to the host gateway on Linux.

Local Python

python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt          # needs system poppler-utils for pdf2image
cp backend/.env.example backend/.env      # then set AI_API_KEY to your OpenRouter key

Repository (Gitea)

Source of truth is Scout IT's Gitea instance:

Remote https://gitea.scoutitsystems.com/woogi/Conflict_Checker.git
Web UI https://gitea.scoutitsystems.com/woogi/Conflict_Checker

Clone:

git clone https://gitea.scoutitsystems.com/woogi/Conflict_Checker.git
cd Conflict_Checker

This repo's git config uses a dedicated credential store at ~/.config/git/gitea-credentials (not your global GitHub credentials). If push/pull prompts for auth, log in via the Gitea web UI and create a personal access token, then store it there or run one authenticated push so the helper saves it.

CI/CD (Gitea Actions → Container Registry)

Develop locally, push to Gitea, and let a runner build/publish the Docker image.

One-time setup

  1. Enable Actions on the repo: Settings → Actions → Enable Repository Actions.
  2. Runner — an act_runner registered against gitea.scoutitsystems.com with the self-hosted label and access to a Docker daemon (typically /var/run/docker.sock mounted into the runner). This instance uses sits-docker-runner (self-hosted, linux, docker).
  3. Registry secret — create a Personal Access Token on Gitea with at least write:package (and write:release if you use version tags). Add it as a repository secret named REGISTRY_TOKEN (Settings → Actions → Secrets).

What runs automatically

Trigger Result
Push to main Image pushed as :latest and :sha-<commit>
Push tag v* (e.g. v0.1.0) Image tagged with the release + Gitea Release created

Image location:

gitea.scoutitsystems.com/woogi/conflict-checker:<tag>

Workflow file: .gitea/workflows/docker-release.yml

Local dev → deploy loop

# 1. Develop and test locally (venv or docker compose build)
git add -A && git commit -m "your change"
git push origin main          # CI builds :latest

# 2. Cut a release when ready
git tag v0.1.0 && git push origin v0.1.0

# 3. On the deploy host — pull and run the published image
docker login gitea.scoutitsystems.com
IMAGE_TAG=v0.1.0 docker compose -f docker-compose.prod.yml up -d
# or: IMAGE_TAG=latest for the newest main-branch build

Copy backend/.env to the deploy host separately (never commit it). Use docker-compose.prod.yml for production; keep docker-compose.yml for local builds from source.

Run

CLI (the fast tuning loop — also dumps assertions.json / clusters.json for inspection):

python cli/run_check.py samples/your_set.pdf --out out/your_set
# -> out/your_set/report.md  +  conflicts.json

Web UI (upload + view):

uvicorn backend.main:app --reload --port 8099   # open http://127.0.0.1:8099

Or use Docker: docker compose up -d (see Setup above).

Conflict categories

dimensional_disagreement, elevation_disagreement, location_mismatch, missing_element, schedule_vs_plan_mismatch, tag_or_reference_inconsistency, spatial_clash, note_or_spec_contradiction.

Tuning

  • Edit prompts/vocab/taxonomy in backend/prompts.py.
  • Inspect out/<set>/assertions.json to see what Stage 1 extracted, and out/<set>/clusters.json to see what got compared. Most false negatives are a clustering/extraction miss; most false positives are a Stage-3 prompt issue.
  • Build a golden set: hand-verify conflicts.json for a known set and diff future runs.

Known limitations (v1)

  • A location appearing in only one discipline never forms a cluster, so pure "missing element" gaps aren't caught deterministically (Stage 3 still catches missing counterparts when the location is co-located).
  • Stage 3 currently sends full pages, not crops. If accuracy/cost needs it, add crop-by-grid later.
S
Description
Cross-discipline design-contradiction checker for construction drawing sets (standalone tool, broken out from Iron_Bid).
Readme
1.3 MiB
Languages
Python 85.9%
HTML 10.5%
Shell 3.2%
Dockerfile 0.4%