AI-powered earthwork quantity takeoff from residential subdivision construction drawings.
Phase 1 MVP targets bid-level accuracy (±5%) for earthwork (cut, fill, stripping) on residential subdivisions.
v2 Hybrid CV/AI Pipeline — deterministic Layer 1 extraction feeds structured data to Layer 2 AI reasoning, replacing full-page vision calls with targeted text prompts. Reduces AI cost per page from $0.05–0.30 to $0.005–0.02.
Next.js (Vercel) → FastAPI (API server) → Redis (arq queue) → arq Worker
↓ ↓
Supabase Auth Layer 1: OpenCV + Tesseract
Supabase DB Layer 2: Claude / GPT-4
S3 / MinIO
takeoff/
├── backend/ # Python 3.11+ (FastAPI + arq worker)
│ ├── app/
│ │ ├── main.py # FastAPI app, CORS, rate limiting, routes
│ │ ├── worker.py # arq worker (CV pipeline, AI calls, S3 I/O)
│ │ ├── auth.py # JWT authentication middleware
│ │ ├── config.py # Settings, ModelConfig, TokenPricing
│ │ ├── database.py # get_service_client / get_user_client (RLS)
│ │ ├── errors.py # Error handling + Sentry integration
│ │ ├── rate_limiter.py # slowapi limits (upload/pipeline/admin)
│ │ ├── models.py # Pydantic request/response models
│ │ ├── constants/
│ │ │ └── earthwork_terms.py # Shared earthwork keyword lists
│ │ ├── routers/ # API route handlers
│ │ │ ├── projects.py # Project CRUD + PDF upload
│ │ │ ├── runs.py # Pipeline trigger, pages, confirmation
│ │ │ └── admin.py # Admin dashboard, traces, golden projects
│ │ ├── services/
│ │ │ ├── projects.py # Project service layer
│ │ │ ├── runs.py # Run service layer
│ │ │ ├── admin.py # Admin metrics, audit log queries
│ │ │ ├── storage.py # S3/MinIO file storage (async)
│ │ │ ├── trace.py # Run trace (what it saw, assumed, spent)
│ │ │ ├── ai_logger.py # AI call logging to ai_call_logs
│ │ │ ├── ocr/ # OCR & text extraction (unified AIProvider)
│ │ │ ├── vision/ # Geometry extraction (unified AIProvider)
│ │ │ ├── graph/ # Drawing Graph construction
│ │ │ ├── rules/ # Rule & assumption engine
│ │ │ ├── earthwork/ # Earthwork computation engine
│ │ │ ├── golden_projects.py # Golden project & accuracy comparison
│ │ │ └── orchestrator.py # Pipeline orchestrator (budget, retry, audit)
│ │ ├── pipeline/
│ │ │ ├── layer1/ # Deterministic CV extraction (Ch18)
│ │ │ │ ├── preprocessor.py # Deskew, adaptive threshold, mask separation
│ │ │ │ ├── line_detector.py # Hough lines + type classification
│ │ │ │ ├── contour_extractor.py # findContours with full hierarchy
│ │ │ │ ├── text_extractor.py # Two-pass Tesseract OCR
│ │ │ │ ├── region_detector.py # Unmatched region detection
│ │ │ │ ├── geometry_assembler.py # Spatial correlation
│ │ │ │ ├── ai_fallback_ocr.py # AI fallback for low-confidence text
│ │ │ │ ├── schema.py # DetectedGeometry v1.0.0 (versioned, hashable)
│ │ │ │ └── runner.py # Parallel page runner (asyncio + semaphore)
│ │ │ └── layer2/ # AI reasoning (Ch19)
│ │ │ ├── provider.py # AIProvider protocol, ClaudeProvider, OpenAIProvider
│ │ │ └── reasoning.py # ReasoningPromptBuilder, Layer 2 orchestration
│ │ └── migrations/ # SQL migrations (001–007)
│ └── tests/ # pytest test suite (600+ tests)
├── frontend/ # Next.js (TypeScript)
│ └── src/
│ ├── app/ # Pages (login, signup, projects, results)
│ ├── components/ # Shared components (auth-guard, overlays)
│ ├── context/ # React context (auth)
│ └── lib/ # Supabase client
├── .github/workflows/ci.yml # CI/CD (GitHub Actions)
├── TRACKER.md # Build progress tracker (Ch1–23)
└── takeoff_layout.md # Architecture spec
PDF Upload
↓
Page Selection UI (thumbnail grid, grading candidate highlighting)
↓
Layer 1: Deterministic CV (OpenCV lines/contours + Tesseract OCR)
↓
Geometry Overlay UI (confirm scale, toggle layers, apply corrections)
↓
Layer 2: AI Reasoning (Claude text prompts from DetectedGeometry)
↓
Earthwork Computation (cut, fill, stripping — deterministic)
↓
Results (confidence, quantities, assumptions, warnings, cost)
cd backend
poetry install
poetry run uvicorn app.main:app --reloadcd backend
poetry run arq app.worker.WorkerSettingsRequires Redis. Set
REDIS_HOST,REDIS_PORT,REDIS_PASSWORDinbackend/.env.
cd frontend
pnpm install
pnpm dev# Backend (600+ tests)
cd backend
poetry run pytest -v
poetry run ruff check .
# Frontend
cd frontend
pnpm test
pnpm lintTests run automatically on every push and pull request via GitHub Actions.
You can also trigger tests manually from the Actions tab (workflow_dispatch).
Copy .env.example files and fill in your values:
backend/.env.example→backend/.envfrontend/.env.example→frontend/.env.local
Key variables (see TRACKER.md for full list):
ANTHROPIC_API_KEY= # Claude Vision + reasoning (primary)
OPENAI_API_KEY= # GPT-4 Vision (fallback)
SUPABASE_URL=
SUPABASE_SERVICE_KEY=
SUPABASE_ANON_KEY= # Per-request user-scoped clients (RLS)
SUPABASE_JWT_SECRET=
REDIS_HOST= # arq job queue
S3_ENDPOINT_URL= # Empty for AWS; http://localhost:9000 for MinIO
Never commit .env files — they contain secrets.
Supabase Postgres S3 / MinIO
├─ users ├─ plan-sets/{project_id}/ (Raw PDFs)
├─ projects ├─ raw-pages/{project_id}/ (Rendered pages)
├─ system_runs └─ exports/{project_id}/ (Large exports)
├─ run_confirmations # Layer 1 geometry confirmations (Ch17)
├─ user_roles # DB-verified admin roles (Ch21)
├─ drawing_graphs
├─ job_progress
├─ results (trace JSON in results.trace)
├─ ai_call_logs # Per-call AI cost/token detail
├─ audit_logs # Stage transitions, user actions
├─ golden_projects # Test project metadata
├─ ground_truth_quantities # Hand-calculated reference values
├─ accuracy_comparisons # Automated test results
├─ golden_geometry_snapshots # Layer 1 DetectedGeometry snapshots (Ch20)
├─ golden_reasoning_expected # Layer 2 expected outputs (Ch20)
├─ golden_expected_quantities # Verified quantities with tolerance bands (Ch20)
└─ golden_validation_runs # Three-level validation results (Ch20)
Trace summarizes. Logs detail.
- Run trace (
results.trace): What it saw, what it assumed, stage durations, cost by stage. Versioned (trace_version). - ai_call_logs: Per-call detail (provider, model, cost, tokens, latency).
- audit_logs: Stage transitions, provider fallback, user confirmations, feedback.
- Sentry: Error tracking with failure resilience (fallback when Sentry itself fails).
All Layer 1 CV detection thresholds are defined in paper inches and converted to
pixel units at runtime via the dpi parameter. Default operating point is 300 DPI.
| DPI | Role | Notes |
|---|---|---|
| 300 | Production (default) | Empirically validated; 300→600 delta < 7% across all feature classes |
| 600 | Convergence reference only | Used to verify 300 DPI is in the stable regime; too slow for production |
| 150 | Legacy / characterization | Distinct lower-quality regime; staircase artifacts inflate line count |
Physical thresholds are defined as constant_in_inches = reference_px / 150 in each
Layer 1 module and converted at __init__ time: px = round(inches * dpi). This
makes the pipeline scale-invariant — changing DPI does not change the physical
detection criteria, only rendering resolution.
See TRACKER.md for detailed chapter-by-chapter progress (Ch1–27 complete).
| Phase | Chapters | Status |
|---|---|---|
| v1 MVP | Ch1–13 | Complete |
| v2 Emergency Security | Ch14–15 | Complete |
| v2 Infrastructure | Ch16–17 | Complete |
| v2 CV/AI Pipeline | Ch18–19 | Complete |
| v2 Golden Framework | Ch20 | Complete |
| v2 Security Hardening | Ch21 | Complete |
| v2 Code Quality | Ch22 | Complete |
| Audit Verification | Ch23 | Complete |
| Layer 1 Determinism Testing | Ch24 | Complete |
| Layer 1 DPI Sensitivity & Physical Parameterization | Ch25 | Complete |
| Three-Tier Golden Suite | Ch26 | Complete |
| Layer 1 Precision — ContourChainStitcher (Gate B/C/D) | Ch27 | Complete |
| Layer 2 Phase 1 — ContourFamilyDetector | Ch28 | In Progress |