# Project export: Neurosymbolic Compliance Engine (NEC)

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2026
- Tagline: An AI-based compliance engine that generalizes across any regulated industry with a config swap. It routes each regulatory check to the right solver.
- Devpost: https://devpost.com/software/civitas-ai-regulon-maybe
- GitHub: https://github.com/ajha7/neurosymbolic-compliance-engine
- Team: 1 GitHub contributor(s) — Anshul Jha (1 commits)

## Devpost submission (written by the team)

### Inspiration

Regulatory systems are everywhere - construction permits, health inspections, financial audits - and they are expensive, slow, and brittle. The first example we targeted was automating city approvals for building permits. Nearly $94,000 of the cost of every new U.S. home comes from regulatory overhead and delays. Major cities report extremely high revision rates on permit submissions due to preventable compliance errors. A single 6-month delay on a mid-sized project with $150,000 monthly carrying costs adds: Almost $1 million lost before construction begins. The deeper issue isn’t just bureaucracy, it’s system design. Regulatory automation today forces a tradeoff: Symbolic rule engines are precise but brittle. Pure LLM systems are flexible but unreliable for strict compliance. We wanted to break that tradeoff. So we built a Neurosymbolic Compliance Engine that combines deterministic logic with controlled AI reasoning without sacrificing the explainbility and trust.

### What it does

Neurosymbolic Compliance Engine(NEC) is generalizable across verticals. It evaluates regulatory documents against inputs needed for compliance. The system routes each rule to the correct reasoning engine: Symbolic Lane → deterministic math & logic (fully reproducible) Hybrid Lane → logic first, AI for exceptions (flagged for review) Neural Lane → structured AI evaluation for subjective standards Every result is categorized as PASS, FAIL, BLOCKED, NEEDS_INFO, or REQUIRES_REVIEW and includes a full trace.

### How we built it

The engine is domain-agnostic and schema-driven. YAML schema defines parameters and rule categories All data collapses into an Evaluation Context: parameter_name → value parameter_name → value Rules are normalized into expression trees A compliance orchestrator: Orders rules via dependency graph (topology sort) Checks applicability Routes to symbolic / hybrid / neural evaluators Feeds computed outputs back into the context Deterministic rules never call an LLM. AI is used only where subjective interpretation is required.

### Challenges we ran into

Neurosymbolic systems are complex to build and we had to write a custom expression language in order to accomodate our queries on the knowledge graph.

### Accomplishments we're proud of

Built a fully domain-agnostic compliance engine Designed a three-lane neurosymbolic routing architecture Achieved deterministic evaluation for numeric and logical rules Built structured AI evaluation with guardrails Created an auditable trace system for every decision Showed horizontal scalability across regulatory verticals

### What we learned

Regulatory friction is often computational, not political. Deterministic systems build trust; AI should augment, not replace logic. Decoupling the reasoning engine from domain knowledge unlocks scalability. Compliance doesn’t need to choose between precision and flexibility, it needs both.

### What's next

for NEC Expand into healthcare, financial, and inspection compliance Improve automated rule extraction pipelines Strengthen confidence calibration for neural evaluations Launch a compliance-as-a-service API The long-term vision: An operating system for regulatory intelligence - where laws are executable, compliance is instant, and review cycles are dramatically reduced.

## README (from the GitHub repository)

No README available.

## Detected evidence (automated analysis)

Indexed codebase: 77 recognized source files, 1333 KB.
- Anthropic (technology) — detected in the code
- CSS (language) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- FastAPI (technology) — claimed on Devpost, not found in the code
- AI coding agent: Claude Code — evidence: config files committed to the repository

## Codebase structure (from repository index)

### Files (100 of 100)

```
.claude/settings.local.json
.gitignore
knowledge_graph_interactive.html
lib/bindings/utils.js
lib/tom-select/tom-select.complete.min.js
lib/tom-select/tom-select.css
lib/vis-9.1.2/vis-network.css
lib/vis-9.1.2/vis-network.min.js
neurosymbolic_compliance_engine/.claude/settings.local.json
neurosymbolic_compliance_engine/application_parser/__init__.py
neurosymbolic_compliance_engine/application_parser/parser.py
neurosymbolic_compliance_engine/cache/graph_adu.json
neurosymbolic_compliance_engine/cache/report_482b5fce.json
neurosymbolic_compliance_engine/cache/report_677f4508.json
neurosymbolic_compliance_engine/cache/report_802ee2a6.json
neurosymbolic_compliance_engine/cache/report_8d670eae.json
neurosymbolic_compliance_engine/cache/report_b5e1b2b2.json
neurosymbolic_compliance_engine/cache/report_d383f6a9.json
neurosymbolic_compliance_engine/cache/report_df5fd23b.json
neurosymbolic_compliance_engine/data_services/__init__.py
neurosymbolic_compliance_engine/data_services/base.py
neurosymbolic_compliance_engine/data_services/geojson_service.py
neurosymbolic_compliance_engine/data/__init__.py
neurosymbolic_compliance_engine/data/rules_adu.py
neurosymbolic_compliance_engine/data/rules_restaurant.py
neurosymbolic_compliance_engine/engine/__init__.py
neurosymbolic_compliance_engine/engine/evaluator.py
neurosymbolic_compliance_engine/engine/hybrid.py
neurosymbolic_compliance_engine/engine/neural.py
neurosymbolic_compliance_engine/engine/orchestrator.py
neurosymbolic_compliance_engine/engine/symbolic.py
neurosymbolic_compliance_engine/engine/types.py
neurosymbolic_compliance_engine/knowledge_graph/__init__.py
neurosymbolic_compliance_engine/knowledge_graph/graph.py
neurosymbolic_compliance_engine/llm/__init__.py
neurosymbolic_compliance_engine/llm/client.py
neurosymbolic_compliance_engine/pipeline.py
neurosymbolic_compliance_engine/report.html
neurosymbolic_compliance_engine/report/__init__.py
neurosymbolic_compliance_engine/report/html.py
neurosymbolic_compliance_engine/report/terminal.py
neurosymbolic_compliance_engine/requirements.txt
neurosymbolic_compliance_engine/rule_extractor/__init__.py
neurosymbolic_compliance_engine/rule_extractor/chunker.py
neurosymbolic_compliance_engine/rule_extractor/extractor.py
neurosymbolic_compliance_engine/rule_extractor/normalizer.py
neurosymbolic_compliance_engine/schemas/__init__.py
neurosymbolic_compliance_engine/schemas/adu_permits.yaml
neurosymbolic_compliance_engine/schemas/restaurant_health.yaml
neurosymbolic_compliance_engine/tests/__init__.py
neurosymbolic_compliance_engine/tests/test_engine_integration.py
neurosymbolic_compliance_engine/tests/test_evaluator.py
neurosymbolic_compliance_engine/tests/test_rules_adu.py
neurosymbolic_compliance_engine/tests/test_symbolic.py
neurosymbolic_compliance_engine/web/backend/__init__.py
neurosymbolic_compliance_engine/web/backend/jobs.py
neurosymbolic_compliance_engine/web/backend/main.py
neurosymbolic_compliance_engine/web/backend/routes.py
neurosymbolic_compliance_engine/web/frontend/.npmrc
neurosymbolic_compliance_engine/web/frontend/index.html
neurosymbolic_compliance_engine/web/frontend/package.json
neurosymbolic_compliance_engine/web/frontend/postcss.config.js
neurosymbolic_compliance_engine/web/frontend/src/App.tsx
neurosymbolic_compliance_engine/web/frontend/src/components/ComplianceReport.tsx
neurosymbolic_compliance_engine/web/frontend/src/components/FileUpload.tsx
neurosymbolic_compliance_engine/web/frontend/src/components/KnowledgeGraphViz.tsx
neurosymbolic_compliance_engine/web/frontend/src/components/RuleCard.tsx
neurosymbolic_compliance_engine/web/frontend/src/index.css
neurosymbolic_compliance_engine/web/frontend/src/main.tsx
neurosymbolic_compliance_engine/web/frontend/tailwind.config.js
neurosymbolic_compliance_engine/web/frontend/tsconfig.json
neurosymbolic_compliance_engine/web/frontend/vite.config.ts
neurosymbolic_compliance_engine/web/run.py
v1/app.py
v1/application_parser.py
v1/application.json
v1/compliance_engine.py
v1/compliance_report.json
v1/gis_service.py
v1/knowledge_graph.json
v1/knowledge_graph.py
v1/main.py
v1/pdf_exporter.py
v1/report_generator.py
v1/report.html
v1/rule_extractor.py
v1/rules.json
v2/application_parser.py
v2/application.json
v2/compliance_engine.py
v2/compliance_report.json
v2/gis_service.py
v2/knowledge_graph.json
v2/knowledge_graph.py
v2/main.py
v2/report_generator.py
v2/report.html
v2/rule_extractor.py
v2/rules.json
visualize_graph.py
```

### Dependencies

- neurosymbolic_compliance_engine/requirements.txt: anthropic, geopandas, jinja2, networkx, pdf2image, pdfplumber, pytesseract, pytest, pyyaml, rich, shapely
- neurosymbolic_compliance_engine/web/frontend/package.json: @types/react@^18.3.1, @types/react-dom@^18.3.1, @vitejs/plugin-react@^4.2.1, autoprefixer@^10.4.18, clsx@^2.1.0, lucide-react@^0.344.0, postcss@^8.4.35, react@^18.3.1, react-dom@^18.3.1, tailwindcss@^3.4.1, typescript@^5.4.2, vis-data@^7.1.9, vis-network@^9.1.9, vite@^5.1.6

### Recent commits (newest first)

- neurosymbolic_compliance_engine - remove secrets and large files

## Key source files (fetched from GitHub, selected and truncated for size)

### neurosymbolic_compliance_engine/requirements.txt

```
anthropic
pdfplumber
pdf2image
pytesseract
networkx
shapely
geopandas
rich
jinja2
pyyaml
pytest

```

### neurosymbolic_compliance_engine/web/frontend/package.json

```
{
  "name": "compliance-engine-ui",
  "private": true,
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "lucide-react": "^0.344.0",
    "clsx": "^2.1.0",
    "vis-network": "^9.1.9",
    "vis-data": "^7.1.9"
  },
  "devDependencies": {
    "@types/react": "^18.3.1",
    "@types/react-dom": "^18.3.1",
    "@vitejs/plugin-react": "^4.2.1",
    "autoprefixer": "^10.4.18",
    "postcss": "^8.4.35",
    "tailwindcss": "^3.4.1",
    "typescript": "^5.4.2",
    "vite": "^5.1.6"
  }
}

```

### v1/main.py

```python
#!/usr/bin/env python3
"""
San Jose ADU Permit Compliance Engine
Main entry point - orchestrates the full pipeline.

Usage:
    python main.py              # Run with demo data
    python main.py --demo       # Run with demo data (explicit)
    python main.py --apn 23712112  # Run with a specific APN
"""

import argparse
import json
import os
import sys
import time

from rich.console import Console
from rich.panel import Panel

# Import modules
from rule_extractor import extract_rules, save_rules
from knowledge_graph import build_knowledge_graph
from application_parser import parse_adu_application, get_demo_site_data, save_application
from gis_service import create_gis_service
from compliance_engine import run_compliance_check, save_report
from report_generator import print_terminal_report, generate_html_report

console = Console()


def run_pipeline(apn: str = None, use_demo: bool = True, skip_gis: bool = False):
    """Run the full ADU compliance pipeline."""

    console.print(Panel(
        "[bold]San Jose ADU Permit Compliance Engine[/bold]\n"
        "Automated Building Permit Compliance Checker\n"
        "Based on Bulletin #210 ADU Universal Checklist",
        title="[blue]TreeHacks 2026[/blue]",
        border_style="blue",
        width=80
    ))
    console.print()

    start_time = time.time()

    # ---- Step 1: Extract Rules ----
    console.print("[bold cyan]Step 1/6:[/bold cyan] Extracting rules from ADU checklist...")
    rules = extract_rules()
    rules_path = save_rules(rules, "rules.json")
    console.print(f"  Extracted [green]{len(rules)}[/green] rules -> {rules_path}")

    # ---- Step 2: Build Knowledge Graph ----
    console.print("[bold cyan]Step 2/6:[/bold cyan] Building knowledge graph...")
    zones = ["R-1-8", "R-1-5", "R-2", "A", "R-M"]
    kg = build_knowledge_graph(rules, zones)
    stats = kg.get_graph_stats()
    kg_path = kg.serialize("knowledge_graph.json")
    console.print(f"  Built graph: [green]{stats['total_nodes']}[/green] nodes, "
                  f"[green]{stats['total_edges']}[/green] edges -> {kg_path}")

    # ---- Step 3: Parse Application ----
    console.print("[bold cyan]Step 3/6:[/bold cyan] Parsing ADU application...")
    if use_demo:
        site_data = get_demo_site_data()
        if apn:
            site_data["parcel_apn"] = apn
        console.print("  Using demo site data")
    else:
        site_data = {"parcel_apn": apn} if apn else {}
        console.print("  Using provided application data")

    application = parse_adu_application(site_overrides=site_data)
    app_path = save_application(application, "application.json")
    specs = application["adu_specs"]
    console.print(f"  ADU: {specs['adu_size_sf']} sf {specs['adu_type']}, "
                  f"{specs['num_stories']} story, {specs['height_ft']} ft -> {app_path}")

    # ---- Step 4: GIS Lookup ----
    console.print("[bold cyan]Step 4/6:[/bold cyan] Looking up GIS data...")
    gis_data = None
    parcel_apn = site_data.get("parcel_apn", apn)

    if skip_gis:
        console.print("  [yellow]GIS lookup skipped[/yellow]")
        # Use fallback data
        gis_data = {
            "found": True,
            "apn": parcel_apn or "23712112",
            "area_sqft": 30293,
            "is_large_lot": True,
            "zoning": "R-1-8",
            "zoning_abbrev": "R-1-8"
        }
    else:
        try:
            gis = create_gis_service()
            if parcel_apn:
                gis_data = gis.get_parcel_info(parcel_apn)
                if gis_data.get("found"):
                    console.print(f"  Parcel: APN {parcel_apn}")
                    console.print(f"  Lot area: [green]{gis_data.get('area_sqft', 0):,.0f}[/green] sf "
                                  f"({'Large' if gis_data.get('is_large_lot') else 'Small'} lot)")
                    console.print(f"  Zoning: [green]{gis_data.get('zoning', 'Unknown')}[/green]")
                else:
                    console.print(f"  [yellow]Parcel {parcel_apn} not found in GIS data[/yellow]")
            else:
                console.print("  [yellow]No APN provided - skipping GIS lookup[/yellow]")
        except Exception as e:
            console.print(f"  [yellow]GIS lookup failed: {e}[/yellow]")
            console.print("  [yellow]Using fallback data[/yellow]")
            gis_data = {
                "found": True,
                "apn": parcel_apn or "23712112",
                "area_sqft": 30293,
                "is_large_lot": True,
                "zoning": "R-1-8"
            }

    # ---- Step 5: Run Compliance Engine ----
    console.print("[bold cyan]Step 5/6:[/bold cyan] Running compliance checks...")
    report = run_compliance_check(application, kg, gis_data)
    report_path = save_report(report, "compliance_report.json")
    console.print(f"  Result: [{'green' if report['overall_result'] == 'PASS' else 'red' if report['overall_result'] == 'FAIL' else 'yellow'}]"
                  f"{report['overall_result']}[/] -> {report_path}")

    # ---- Step 6: Generate Reports ----
    console.print("[bold cyan]Step 6/6:[/bold cyan] Generating reports...")
    html_path = generate_html_report(report, "report.html")
    console.print(f"  HTML report -> {html_path}")

    elapsed = time.time() - start_time
    console.print(f"\n[dim]Pipeline completed in {elapsed:.1f}s[/dim]\n")

    # Print terminal report
    console.print("=" * 80)
    print_terminal_report(report)

    return report


def main():
    parser = argparse.ArgumentParser(
        description="San Jose ADU Permit Compliance Engine",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  python main.py                    Run with demo data (500 SF ADU plan)
  python main.py --apn 23712112     Run with specific APN
  python main.py --skip-gis         Skip GIS data loading (faster)
        """
    )
    parser.add_argument("--demo", action="store_true", default=True,
                        help="Use demo d
[truncated — 501 more characters]
```

### v2/main.py

```python
#!/usr/bin/env python3
"""
San Jose ADU Permit Compliance Engine
Main entry point - orchestrates the full pipeline.

Usage:
    python main.py              # Run with demo data
    python main.py --demo       # Run with demo data (explicit)
    python main.py --apn 23712112  # Run with a specific APN
"""

import argparse
import json
import os
import sys
import time

from rich.console import Console
from rich.panel import Panel

# Import modules
from rule_extractor import extract_rules, save_rules
from knowledge_graph import build_knowledge_graph
from application_parser import parse_adu_application, get_demo_site_data, save_application
from gis_service import create_gis_service
from compliance_engine import run_compliance_check, save_report
from report_generator import print_terminal_report, generate_html_report

console = Console()


def run_pipeline(apn: str = None, use_demo: bool = True, skip_gis: bool = False):
    """Run the full ADU compliance pipeline."""

    console.print(Panel(
        "[bold]San Jose ADU Permit Compliance Engine[/bold]\n"
        "Automated Building Permit Compliance Checker\n"
        "Based on Bulletin #210 ADU Universal Checklist",
        title="[blue]TreeHacks 2026[/blue]",
        border_style="blue",
        width=80
    ))
    console.print()

    start_time = time.time()

    # ---- Step 1: Extract Rules ----
    console.print("[bold cyan]Step 1/6:[/bold cyan] Extracting rules from ADU checklist...")
    rules = extract_rules()
    rules_path = save_rules(rules, "rules.json")
    console.print(f"  Extracted [green]{len(rules)}[/green] rules -> {rules_path}")

    # ---- Step 2: Build Knowledge Graph ----
    console.print("[bold cyan]Step 2/6:[/bold cyan] Building knowledge graph...")
    zones = ["R-1-8", "R-1-5", "R-2", "A", "R-M"]
    kg = build_knowledge_graph(rules, zones)
    stats = kg.get_graph_stats()
    kg_path = kg.serialize("knowledge_graph.json")
    console.print(f"  Built graph: [green]{stats['total_nodes']}[/green] nodes, "
                  f"[green]{stats['total_edges']}[/green] edges -> {kg_path}")

    # ---- Step 3: Parse Application ----
    console.print("[bold cyan]Step 3/6:[/bold cyan] Parsing ADU application...")
    if use_demo:
        site_data = get_demo_site_data()
        if apn:
            site_data["parcel_apn"] = apn
        console.print("  Using demo site data")
    else:
        site_data = {"parcel_apn": apn} if apn else {}
        console.print("  Using provided application data")

    application = parse_adu_application(site_overrides=site_data)
    app_path = save_application(application, "application.json")
    specs = application["adu_specs"]
    console.print(f"  ADU: {specs['adu_size_sf']} sf {specs['adu_type']}, "
                  f"{specs['num_stories']} story, {specs['height_ft']} ft -> {app_path}")

    # ---- Step 4: GIS Lookup ----
    console.print("[bold cyan]Step 4/6:[/bold cyan] Looking up GIS data...")
    gis_data = None
    parcel_apn = site_data.get("parcel_apn", apn)

    if skip_gis:
        console.print("  [yellow]GIS lookup skipped[/yellow]")
        # Use fallback data
        gis_data = {
            "found": True,
            "apn": parcel_apn or "23712112",
            "area_sqft": 30293,
            "is_large_lot": True,
            "zoning": "R-1-8",
            "zoning_abbrev": "R-1-8"
        }
    else:
        try:
            gis = create_gis_service()
            if parcel_apn:
                gis_data = gis.get_parcel_info(parcel_apn)
                if gis_data.get("found"):
                    console.print(f"  Parcel: APN {parcel_apn}")
                    console.print(f"  Lot area: [green]{gis_data.get('area_sqft', 0):,.0f}[/green] sf "
                                  f"({'Large' if gis_data.get('is_large_lot') else 'Small'} lot)")
                    console.print(f"  Zoning: [green]{gis_data.get('zoning', 'Unknown')}[/green]")
                else:
                    console.print(f"  [yellow]Parcel {parcel_apn} not found in GIS data[/yellow]")
            else:
                console.print("  [yellow]No APN provided - skipping GIS lookup[/yellow]")
        except Exception as e:
            console.print(f"  [yellow]GIS lookup failed: {e}[/yellow]")
            console.print("  [yellow]Using fallback data[/yellow]")
            gis_data = {
                "found": True,
                "apn": parcel_apn or "23712112",
                "area_sqft": 30293,
                "is_large_lot": True,
                "zoning": "R-1-8"
            }

    # ---- Step 5: Run Compliance Engine ----
    console.print("[bold cyan]Step 5/6:[/bold cyan] Running compliance checks...")
    report = run_compliance_check(application, kg, gis_data)
    report_path = save_report(report, "compliance_report.json")
    console.print(f"  Result: [{'green' if report['overall_result'] == 'PASS' else 'red' if report['overall_result'] == 'FAIL' else 'yellow'}]"
                  f"{report['overall_result']}[/] -> {report_path}")

    # ---- Step 6: Generate Reports ----
    console.print("[bold cyan]Step 6/6:[/bold cyan] Generating reports...")
    html_path = generate_html_report(report, "report.html")
    console.print(f"  HTML report -> {html_path}")

    elapsed = time.time() - start_time
    console.print(f"\n[dim]Pipeline completed in {elapsed:.1f}s[/dim]\n")

    # Print terminal report
    console.print("=" * 80)
    print_terminal_report(report)

    return report


def main():
    parser = argparse.ArgumentParser(
        description="San Jose ADU Permit Compliance Engine",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  python main.py                    Run with demo data (500 SF ADU plan)
  python main.py --apn 23712112     Run with specific APN
  python main.py --skip-gis         Skip GIS data loading (faster)
        """
    )
    parser.add_argument("--demo", action="store_true", default=True,
                        help="Use demo d
[truncated — 501 more characters]
```

### v1/app.py

```python
"""
San Jose ADU Permit Compliance Engine - Streamlit Web UI
Full-featured web interface with interactive map, side-by-side standards comparison,
PDF export, and file upload support.
"""

import streamlit as st
import json
import os
import sys
import time
import tempfile
from datetime import datetime

import folium
from streamlit_folium import st_folium
import geopandas as gpd
from shapely.geometry import mapping

# Ensure local imports work
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

from rule_extractor import extract_rules
from knowledge_graph import build_knowledge_graph
from application_parser import parse_adu_application, get_demo_site_data
from gis_service import create_gis_service
from compliance_engine import run_compliance_check
from pdf_exporter import generate_pdf_report

# ---- Page Config ----
st.set_page_config(
    page_title="ADU Permit Compliance Engine",
    page_icon=":house:",
    layout="wide",
    initial_sidebar_state="expanded"
)

# ---- Custom CSS ----
st.markdown("""
<style>
    .main-header {
        background: linear-gradient(135deg, #1a365d, #2b6cb0);
        color: white;
        padding: 1.5rem 2rem;
        border-radius: 12px;
        margin-bottom: 1.5rem;
    }
    .main-header h1 { color: white; margin: 0; font-size: 1.8rem; }
    .main-header p { color: rgba(255,255,255,0.85); margin: 0.25rem 0 0 0; font-size: 0.95rem; }
    .status-pass {
        background: #c6f6d5; color: #22543d;
        padding: 4px 12px; border-radius: 6px;
        font-weight: 700; font-size: 0.85rem; display: inline-block;
    }
    .status-fail {
        background: #fed7d7; color: #742a2a;
        padding: 4px 12px; border-radius: 6px;
        font-weight: 700; font-size: 0.85rem; display: inline-block;
    }
    .status-info {
        background: #fefcbf; color: #744210;
        padding: 4px 12px; border-radius: 6px;
        font-weight: 700; font-size: 0.85rem; display: inline-block;
    }
    .result-pass { background: #c6f6d5; border: 2px solid #38a169; border-radius: 12px; padding: 1rem 1.5rem; }
    .result-fail { background: #fed7d7; border: 2px solid #e53e3e; border-radius: 12px; padding: 1rem 1.5rem; }
    .result-conditional { background: #fefcbf; border: 2px solid #d69e2e; border-radius: 12px; padding: 1rem 1.5rem; }
    .metric-card {
        background: white; border: 1px solid #e2e8f0; border-radius: 10px;
        padding: 1rem; text-align: center; box-shadow: 0 1px 3px rgba(0,0,0,0.05);
    }
    .metric-card .number { font-size: 2rem; font-weight: 700; }
    .metric-card .label { font-size: 0.8rem; color: #718096; }
    div[data-testid="stExpander"] { border: 1px solid #e2e8f0; border-radius: 8px; margin-bottom: 0.5rem; }
</style>
""", unsafe_allow_html=True)


# ---- Cached Data Loading ----
@st.cache_resource
def load_rules():
    return extract_rules()

@st.cache_resource
def load_knowledge_graph(_rules):
    zones = ["R-1-8", "R-1-5", "R-2", "A", "R-M", "HI", "CG", "CP"]
    return build_knowledge_graph(_rules, zones)

@st.cache_resource
def load_gis_service():
    return create_gis_service()


def get_status_badge(status):
    if status == "PASS":
        return '<span class="status-pass">PASS</span>'
    elif status == "FAIL":
        return '<span class="status-fail">FAIL</span>'
    return '<span class="status-info">NEEDS INFO</span>'


def render_header():
    st.markdown("""
    <div class="main-header">
        <h1>San Jose ADU Permit Compliance Engine</h1>
        <p>Automated building permit compliance checker based on Bulletin #210 ADU Universal Checklist</p>
    </div>
    """, unsafe_allow_html=True)


def render_sidebar():
    """Render the sidebar with application inputs."""
    st.sidebar.markdown("## Application Input")

    # Mode selection
    mode = st.sidebar.radio(
        "Input Mode",
        ["Demo Data (500 SF ADU)", "Custom Input"],
        help="Use pre-loaded demo data or enter custom values"
    )

    if mode == "Demo Data (500 SF ADU)":
        site_data = get_demo_site_data()
        adu_specs = {
            "adu_type": "detached",
            "adu_size_sf": 500,
            "num_stories": 1,
            "height_ft": 16,
        }
        st.sidebar.success("Using demo: 500 SF detached ADU plan")
    else:
        st.sidebar.markdown("### ADU Specifications")
        adu_type = st.sidebar.selectbox("ADU Type", ["detached", "attached"], index=0)
        adu_size = st.sidebar.number_input("ADU Size (sf)", 150, 1200, 500, step=50)
        num_stories = st.sidebar.selectbox("Number of Stories", [1, 2], index=0)
        height_ft = st.sidebar.number_input("Height (ft)", 8, 25, 16, step=1)

        adu_specs = {
            "adu_type": adu_type,
            "adu_size_sf": adu_size,
            "num_stories": num_stories,
            "height_ft": height_ft,
        }

        st.sidebar.markdown("### Site Data")
        apn = st.sidebar.text_input("Parcel APN", "23712112")
        property_type = st.sidebar.selectbox("Property Type", ["single_family", "duplex", "multifamily"], index=0)
        standard = st.sidebar.selectbox("Standard", ["city", "state"], index=0)

        st.sidebar.markdown("### Eligibility")
        in_sj = st.sidebar.checkbox("Property in San Jose?", True)
        permitted = st.sidebar.checkbox("Main home permitted?", True)
        code_issues = st.sidebar.checkbox("Code enforcement issues?", False)

        st.sidebar.markdown("### Property Designations")
        flood = st.sidebar.selectbox("Flood Zone", ["X", "D", "A", "AE", "AH", "AO"], index=0)
        geohazard = st.sidebar.checkbox("Geohazard zone?", False)
        historic = st.sidebar.checkbox("Historic property?", False)
        wui = st.sidebar.checkbox("WUI zone?", False)
        easement = st.sidebar.checkbox("Has easement?", False)
        nonbuild = st.sidebar.checkbox("Nonbuildable area?", False)

        st.sidebar.markdown("### Fire Safety")
        addr_visible = st.sidebar.checkbox("Address visible from street?", True
[truncated — 14402 more characters]
```

### neurosymbolic_compliance_engine/web/backend/main.py

```python
"""FastAPI application for the neurosymbolic compliance engine."""
from __future__ import annotations

import sys
from pathlib import Path

# Ensure project root is on the path
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
sys.path.insert(0, str(PROJECT_ROOT))

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles

from web.backend.routes import router

app = FastAPI(
    title="Neurosymbolic Compliance Engine",
    description="Regulatory compliance evaluation with symbolic + neural reasoning",
    version="1.0.0",
)

# CORS for local development
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000", "http://localhost:5173", "http://127.0.0.1:3000", "http://127.0.0.1:5173"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

app.include_router(router)


@app.get("/")
async def root():
    return {"message": "Neurosymbolic Compliance Engine API", "docs": "/docs"}


if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

```

### neurosymbolic_compliance_engine/web/frontend/src/main.tsx

```typescript
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)

```

### neurosymbolic_compliance_engine/web/frontend/src/App.tsx

```typescript
import { useState, useCallback } from 'react'
import FileUpload from './components/FileUpload'
import ComplianceReport from './components/ComplianceReport'
import KnowledgeGraphViz from './components/KnowledgeGraphViz'
import { Shield, GitBranch, FileText } from 'lucide-react'

type PipelineStage = 'idle' | 'parsing' | 'graph_lookup' | 'evaluating' | 'reporting' | 'complete' | 'failed'

interface JobStatus {
  job_id: string
  stage: PipelineStage
  progress: number
  message: string
  error?: string
}

const STAGE_LABELS: Record<string, string> = {
  idle: 'Ready',
  queued: 'Queued',
  parsing: 'Parsing Application',
  graph_lookup: 'Resolving Dependencies',
  evaluating: 'Evaluating Rules',
  reporting: 'Generating Report',
  complete: 'Complete',
  failed: 'Failed',
}

const STAGE_ORDER = ['parsing', 'graph_lookup', 'evaluating', 'reporting', 'complete']

export default function App() {
  const [activeTab, setActiveTab] = useState<'evaluate' | 'graph'>('evaluate')
  const [domain, setDomain] = useState<'adu' | 'restaurant'>('adu')
  const [jobStatus, setJobStatus] = useState<JobStatus | null>(null)
  const [report, setReport] = useState<any>(null)
  const [isPolling, setIsPolling] = useState(false)
  const [isUploading, setIsUploading] = useState(false)

  const pollJob = useCallback(async (jobId: string) => {
    setIsPolling(true)
    const poll = async () => {
      try {
        const res = await fetch(`/api/status/${jobId}`)
        const data: JobStatus = await res.json()
        setJobStatus(data)

        if (data.stage === 'complete') {
          const reportRes = await fetch(`/api/report/${jobId}`)
          const reportData = await reportRes.json()
          setReport(reportData)
          setIsPolling(false)
          return
        }
        if (data.stage === 'failed') {
          setIsPolling(false)
          return
        }
        setTimeout(poll, 500)
      } catch {
        setIsPolling(false)
      }
    }
    poll()
  }, [])

  const handleSubmit = useCallback(async (file: File | null, paramsJson: string, selectedDomain: string) => {
    setReport(null)
    setJobStatus(null)
    setDomain(selectedDomain as 'adu' | 'restaurant')
    setIsUploading(true)

    const formData = new FormData()
    formData.append('domain', selectedDomain)
    if (file) formData.append('file', file)
    if (paramsJson) formData.append('params_json', paramsJson)

    try {
      const res = await fetch('/api/upload', { method: 'POST', body: formData })
      setIsUploading(false)
      if (!res.ok) {
        const err = await res.json()
        setJobStatus({ job_id: '', stage: 'failed', progress: 0, message: '', error: err.detail })
        return
      }
      const { job_id } = await res.json()
      setJobStatus({ job_id, stage: 'parsing', progress: 0.05, message: 'Starting...' })
      pollJob(job_id)
    } catch (e: any) {
      setIsUploading(false)
      setJobStatus({ job_id: '', stage: 'failed', progress: 0, message: '', error: e.message })
    }
  }, [pollJob])

  const currentStageIdx = jobStatus ? STAGE_ORDER.indexOf(jobStatus.stage) : -1

  return (
    <div className="min-h-screen bg-gray-50">
      {/* Header */}
      <header className="bg-white border-b border-gray-200 shadow-sm">
        <div className="max-w-7xl mx-auto px-4 py-4 flex items-center justify-between">
          <div className="flex items-center gap-3">
            <Shield className="w-8 h-8 text-indigo-600" />
            <div>
              <h1 className="text-xl font-bold text-gray-900">Neurosymbolic Compliance Engine</h1>
              <p className="text-sm text-gray-500">Symbolic + Neural regulatory evaluation</p>
            </div>
          </div>
          <div className="flex gap-1 bg-gray-100 rounded-lg p-1">
            <button
              onClick={() => setActiveTab('evaluate')}
              className={`px-4 py-2 rounded-md text-sm font-medium transition-colors flex items-center gap-2 ${
                activeTab === 'evaluate' ? 'bg-white shadow text-gray-900' : 'text-gray-600 hover:text-gray-900'
              }`}
            >
              <FileText className="w-4 h-4" /> Evaluate
            </button>
            <button
              onClick={() => setActiveTab('graph')}
              className={`px-4 py-2 rounded-md text-sm font-medium transition-colors flex items-center gap-2 ${
                activeTab === 'graph' ? 'bg-white shadow text-gray-900' : 'text-gray-600 hover:text-gray-900'
              }`}
            >
              <GitBranch className="w-4 h-4" /> Knowledge Graph
            </button>
          </div>
        </div>
      </header>

      <main className="max-w-7xl mx-auto px-4 py-6">
        {activeTab === 'evaluate' && (
          <div className="space-y-6">
            <FileUpload onSubmit={handleSubmit} isProcessing={isPolling} isUploading={isUploading} />

            {/* Progress indicator */}
            {jobStatus && jobStatus.stage !== 'idle' && (
              <div className="bg-white rounded-xl border border-gray-200 p-6 shadow-sm">
                <div className="flex items-center justify-between mb-4">
                  <h3 className="font-semibold text-gray-900">Pipeline Progress</h3>
                  <span className={`text-sm font-medium px-3 py-1 rounded-full ${
                    jobStatus.stage === 'complete' ? 'bg-green-100 text-green-700' :
                    jobStatus.stage === 'failed' ? 'bg-red-100 text-red-700' :
                    'bg-blue-100 text-blue-700'
                  }`}>
                    {STAGE_LABELS[jobStatus.stage] || jobStatus.stage}
                  </span>
                </div>

                {/* Stage progress bar */}
                <div className="flex items-center gap-2 mb-3">
                  {STAGE_ORDER.map((stage, i) => {
                    const isActive = stage === jobStatus.stage
                    const isDone = currentStageIdx > i || jobStatus.stage === 'complete'
                    return (
  
[truncated — 1258 more characters]
```

### visualize_graph.py

```python
"""
Visualize the ADU Compliance Knowledge Graph using Pyvis.
Run: python visualize_graph.py
Requires: pip install pyvis
"""

from pyvis.network import Network
import json

# Load the knowledge graph
with open("knowledge_graph.json") as f:
    data = json.load(f)

# Create interactive network
net = Network(height="900px", width="100%", directed=True, bgcolor="#1a1a2e", font_color="white")

# Enable physics for force-directed layout
net.barnes_hut(gravity=-3000, central_gravity=0.3, spring_length=150)

# Color scheme by node type
color_map = {
    "CategoryNode": "#ff6b6b",
    "RuleNode": "#4ecdc4",
    "ParameterNode": "#ffe66d",
    "ZoneNode": "#95e1d3"
}

size_map = {
    "CategoryNode": 30,
    "RuleNode": 20,
    "ParameterNode": 10,
    "ZoneNode": 25
}

# Add nodes
for node in data["nodes"]:
    node_type = node.get("node_type", "Unknown")
    label = node.get("name", node["id"])
    if len(label) > 25:
        label = label[:22] + "..."
    
    # Build tooltip
    tooltip = f"<b>{node['id']}</b><br>Type: {node_type}"
    if "description" in node:
        tooltip += f"<br>{node['description']}"
    
    net.add_node(
        node["id"],
        label=label,
        color=color_map.get(node_type, "#888"),
        size=size_map.get(node_type, 15),
        title=tooltip,
        shape="dot" if node_type != "CategoryNode" else "diamond"
    )

# Edge colors by type
edge_colors = {
    "BELONGS_TO": "#888",
    "HAS_PARAMETER": "#ffe66d",
    "APPLIES_TO_ZONE": "#95e1d3",
    "DEPENDS_ON": "#ff6b6b"
}

# Add edges
for edge in data["edges"]:
    edge_type = edge.get("edge_type", "")
    net.add_edge(
        edge["source"],
        edge["target"],
        title=edge_type,
        color=edge_colors.get(edge_type, "#666"),
        arrows="to"
    )

# Generate HTML
net.show("knowledge_graph_interactive.html", notebook=False)
print("✅ Generated: knowledge_graph_interactive.html")
print(f"   Nodes: {len(data['nodes'])}, Edges: {len(data['edges'])}")
```

### v1/application_parser.py

```python
"""
Module 4: Application Parser
Parses ADU plan specifications and accepts manual site-specific overrides.
For the demo, hardcodes the 500 SF ADU plan specs from the San Joaquin County plans.
"""

import json
from typing import Dict, Any, Optional


def parse_adu_application(plan_path: Optional[str] = None,
                          site_overrides: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
    """
    Parse an ADU application from plan documents and site data.

    For the hackathon demo, plan specs are hardcoded from the 500 SF ADU plan.
    Site-specific data must be provided via overrides since it can't be
    extracted from construction plans alone.
    """
    # Hardcoded specs from the 500 SF ADU construction plans
    # Extracted from sheets A0.0, A1, A3, A4, A5
    application = {
        "plan_info": {
            "plan_name": "Plan 2 - 500 SF Accessory Dwelling Unit",
            "source": "San Joaquin County, Planning & Development Services",
            "sheets": ["A0.0", "A0.1", "A1", "A1.1", "A2", "A3", "A4", "A5",
                       "S0", "S0.1", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "CS-1"]
        },
        "adu_specs": {
            "adu_type": "detached",
            "adu_size_sf": 500,
            "num_stories": 1,
            "height_ft": 16,
            "footprint": {
                "width_ft": 25.0,
                "depth_ft": 24.75
            },
            "roof_pitch": "6.5:12",
            "construction_type": "conventional light frame",
            "has_loft": True,
            "has_sprinklers": None  # Unknown / TBD from plans
        },
        "structural_info": {
            "roof_live_load_psf": 20,
            "wind_speed_mph": 110,
            "exposure_category": "C",
            "site_class": "D",
            "risk_category": "II",
            "sds": 1.25,
            "seismic_design_category": "D2",
            "soil_bearing_psf": 1500
        },
        "project_scope": "Proposed 500 SF detached accessory dwelling unit",
        "site_data": {}
    }

    # Apply site-specific overrides
    if site_overrides:
        application["site_data"] = site_overrides

    return application


def get_demo_site_data() -> Dict[str, Any]:
    """Return demo site-specific data for the test scenario."""
    return {
        "parcel_apn": "23712112",
        "property_in_san_jose": True,
        "main_home_permitted": True,
        "code_enforcement_issues": False,
        "flood_zone": "X",
        "geohazard": False,
        "historic": False,
        "wui_zone": False,
        "has_easement": False,
        "nonbuildable_area": False,
        "standard_chosen": "city",
        "property_type": "single_family",
        "distance_to_street_curb_ft": 120,
        "distance_to_hydrant_ft": 400,
        "hydrant_flow_gpm": 1200,
        "primary_has_sprinklers": False,
        "primary_home_sf": 1800,
        "trees_to_remove": False,
        "existing_address_visible": True
    }


def save_application(application: Dict[str, Any],
                     output_path: str = "application.json") -> str:
    """Save parsed application to JSON."""
    with open(output_path, "w") as f:
        json.dump(application, f, indent=2)
    return output_path


if __name__ == "__main__":
    site_data = get_demo_site_data()
    app = parse_adu_application(site_overrides=site_data)
    output = save_application(app)
    print(f"Application parsed and saved to {output}")
    print(f"\nADU Specs:")
    for k, v in app["adu_specs"].items():
        print(f"  {k}: {v}")
    print(f"\nSite Data:")
    for k, v in app["site_data"].items():
        print(f"  {k}: {v}")

```

[55 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]