# Project export: Constructa

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: UC Berkeley AI Hackathon 2026
- Tagline: Constructa helps developers and contracting teams evaluate buildable sites, visualize projects, and complete permit paperwork faster so they can build with fewer costly delays.
- Devpost: https://devpost.com/software/constructa
- GitHub: https://github.com/vynx1/Constructa
- Video: https://www.youtube.com/embed/Wa2UlZcN85A?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Result: winner (Best Use of The Agentverse by Fetch AI; Best Use of Browserbase)
- Team: 5 GitHub contributor(s) — Shubhay (20 commits), niranjan-deshpande1 (5 commits), Cursor (5 commits), aadij997-blip (4 commits), shivampansuria (2 commits)

## Devpost submission (written by the team)

### Inspiration

We are from California, where construction delays are not an abstract problem; we see them constantly in daily life. Housing, public infrastructure, and commercial projects often run over budget or take years longer than expected, with McKinsey finding an average of one year behind schedule and 30% over budget, and a major reason is how difficult it is to understand and comply with overlapping regulations. Those delays do not just hurt developers; they affect citizens through higher costs, fewer available buildings, and slower community improvements. In fact, a study of Los Angeles multifamily housing projects found that reducing development approval times by just 25% would have increased housing production by 12.7%. We started with a simple question: why is it still so hard for construction teams to understand what land is buildable, what regulations apply, and what documents need to be completed? There had to be a simpler way to turn scattered zoning, permit, and compliance information into a usable workflow. That was the inspiration for Constructa. It evolved from a regulation-simplification idea into an end-to-end tool that helps teams find potential sites, evaluate them, visualize a project, and then move through the construction workflow with AI support.

### What it does

Constructa is a dashboard for contractors and project managers that answers the questions of "where should we build?" and "how do we actually get it permitted and built?" Users can explore undeveloped or underused land, compare sites using feasibility scores, and review source-backed factors such as zoning availability, regulatory complexity, hazards, infrastructure, market context, and permit-related constraints. Once a site is selected, a project manager can describe the proposed build in natural language. Constructa turns that input into a 3D model of the proposal in the selected area, along with a click-through project timeline. The timeline walks through pre-construction compliance, foundation, structure, systems/MEP, envelope and facade, and finish/closeout. Each stage includes estimated cost, estimated timeline, relevant regulations, and a regulatory requirements checklist. Users can also open a live assistant for each stage, with expert, trained agents that answer specific questions about action items, forms, risks, or next steps. At the end, a “Permit Forms” button auto-fills documents including Cal/OSHA DOSH 41-1 and construction RFI’s using the project intake and inferred permit/zoning context.

### How we built it

Constructa is a data-first full-stack app with two surfaces: a land-intelligence map and a project workspace that walks a build through compliance step by step. Browserbase powers the map's live research. When someone drills into a region, we drive a cloud Chromium session through geo-targeted residential proxies, scrape public context (listings, permit and zoning signals, local discussion), compress away the noise, and feed grounded data into scoring and recommendations. Fetch.ai (ASI:One + Agentverse) is the brain. The land guides, compliance plans, 3D models, and stage-level answers all route through ASI:One. ASI:One acts as lead orchestrator and synthesizes 6 custom uploaded Agentverse agents into one buy/hold/avoid call, and a low-confidence agent gets re-queried once before it's trusted. Arize sits underneath all of it as our observability layer. Every agent call is instrumented with success and error spans, so we can see exactly which agent failed, stalled, or returned weak output. Pairing that visibility with the fallback pattern is what keeps the experience smooth: a logged error gets caught and answered with a deterministic result natively. In the workspace, a natural-language building description becomes a 3D model powered by ASI:One who creates a Three.js component instructions that we render with react-three-fiber. The timeline then walks permits → foundation → structure → systems → finish, with the camera focusing on the matching part of the model at each step. Six agent actions (daily log, RFI, compliance, permit research, hazards, model edit) all share the same try → log → fallback contract; cleared steps emit the compliance and permit PDFs. Redis is the database that keeps both surfaces fast and in sync—it caches map scores, scraped research, liked plots, generated 3D models, and project plans, each with its own TTL. Repeat lookups that would otherwise re-run an expensive scrape or a six-agent synthesis return instantly from cache, which is the difference between an interactive dashboard and a spinner. And the whole thing degrades cleanly without live APIs: seeded data and deterministic fallbacks keep the full flow alive on stage.

### Challenges we ran into

Pulling useful live data off the open web, especially images. Every site structures land listings, zoning details, and local context differently, so extracting the same fields consistently was brutal. We also couldn't lean on local cache; repeated lookups on an interactive dashboard would crawl, so scraped results land in Redis with TTLs instead. Browserbase became the cleanest way to automate extraction without hand-coding every site's quirks, and it let us resolve real listing images (via currentSrc after the page settled) rather than placeholder thumbnails. Pulling useful live data off the open web, especially images. Every site structures land listings, zoning details, and local context differently, so extracting the same fields consistently was brutal. We also couldn't lean on local cache; repeated lookups on an interactive dashboard would crawl, so scraped results land in Redis with TTLs instead. Browserbase became the cleanest way to automate extraction without hand-coding every site's quirks, and it let us resolve real listing images (via currentSrc after the page settled) rather than placeholder thumbnails. Reliability: live scraping and LLM calls fail constantly. We treated failure as expected and engineered failsafes. Arize surfaces failed and low-confidence calls per agent, and ASI:One + Agentverse give us a tiered fallback: multistack consensus first, single-agent delegation second, deterministic seeded data last. The result is a system where a flaky network or a bad model response never reaches the user. Reliability: live scraping and LLM calls fail constantly. We treated failure as expected and engineered failsafes. Arize surfaces failed and low-confidence calls per agent, and ASI:One + Agentverse give us a tiered fallback: multistack consensus first, single-agent delegation second, deterministic seeded data last. The result is a system where a flaky network or a bad model response never reaches the user. 3D model generation. Storing and rendering real 3D asset files would be slow and expensive, so instead of shipping geometry we have ASI:One generate Three.js component instructions. Turning free-form language into precise 3D placement threw a lot of errors early on, so we constrained the problem: a dictionary of pre-built, parameterized components—trees, pools, roads, tennis courts, structures—that the model can place and tune safely, instead of inventing geometry from scratch. 3D model generation. Storing and rendering real 3D asset files would be slow and expensive, so instead of shipping geometry we have ASI:One generate Three.js component instructions. Turning free-form language into precise 3D placement threw a lot of errors early on, so we constrained the problem: a dictionary of pre-built, parameterized components—trees, pools, roads, tennis courts, structures—that the model can place and tune safely, instead of inventing geometry from scratch.

### Accomplishments we're proud of

1) We are proud that we used new tools like Browserbase and ASI in a way that actually interacted with one another, instead of just adding them as isolated integrations. Browserbase helped us pull live web context, Redis helped us store and reuse it quickly, and ASI:One helped route, synthesize, and recover agent workflows. It let us explore what AI can do when it is embedded into a real workflow. 2) The “Permit Forms” feature is another highlight because it makes the value concrete. Instead of only telling a project manager what regulations exist, Constructa starts doing the paperwork: filling a Cal/OSHA DOSH 41-1 and construction RFIs from the project details. That is the kind of practical time-saving workflow we wanted to build. 3) More than anything, we are proud that Constructa became more than a workflow optimization tool. It does not only reduce workload or regulatory delay for a principal contractor or developer. It starts with finding locations and continues into during-work support that can help different members of a contracting team: principal contractors, lead architects, project managers, and site foremen.

### What we learned

1) We learned the importance of grounding AI outputs in sources and structured data. A site score is only useful if users can understand why the score exists. A compliance checklist is only useful if it connects back to the project type, location, and inferred requirements. That pushed us to design the product around source pulling, caching, structured project documents, and visible reasoning rather than one-off chatbot responses. 2) Technically, we learned that building reliable AI products means designing workarounds before things fail. For scraping, that meant using Browserbase and storing results in Redis so live data could become fast reusable context. For agents, that meant adding Arize and ASI fallback behavior. For 3D generation, that meant limiting the AI to a component dictionary instead of letting it invent arbitrary geometry. 3) We also learned from talking to a mentor that construction users may not want to type everything. People on contracting teams are often on-site, on the go, or, especially, unfamiliar with AI tools, so voice-to-text could make the product much more natural for foremen and field workers. Overall, it was an amazing experience building something that feels genuinely useful. That was the point of AI for us: saving time on repetitive, confusing work so people can focus on building.

### What's next

Next, we want Constructa to become a shared workspace for the entire contracting team. That could mean a Slack-style workspace inside the product or integrations with tools teams already use, so architects, project managers, site foremen, compliance leads, and contractors can collaborate around the same project data. We also want to make the product role-aware. An architect logging in should see different tools than a project manager or site foreman. Architects might get deeper design and code-compliance guidance, project managers might get cost, timeline, and permit-risk tracking, and foremen might get voice-first task logging, safety reminders, and field-specific assistants. We would also expand the forms and compliance system beyond the initial Cal/OSHA and RFI workflows into broader permit packets, inspection readiness, stormwater, closeout, and safety documentation. The long-term goal is to make sure everyone on a contracting team stays on top of regulations while getting AI help that is actually relevant to their role; this is something still not effectively integrated into the construction field today.

## README (from the GitHub repository)

#PS: Deployment Will Not Work due to API Key's. Please watch the Demo video at the Devpost here instead: 
https://devpost.com/software/constructa?_gl=1*h2wsbf*_gcl_au*MTIyOTUwMDE2Ni4xNzc3NzUyNzM4*_ga*MjAzOTY4MjA4NC4xNzc3NzUyNzM4*_ga_0YHJK3Y10M*czE3ODIzMjA1MjEkbzkkZzEkdDE3ODIzMjA1NTUkajI2JGwwJGgw


## Stack

| Layer        | Choice                                              |
| ------------ | --------------------------------------------------- |
| Framework    | TanStack Start (React, SSR, file routing)           |
| Data         | TanStack Query                                      |
| 3D           | Three.js via React Three Fiber + drei               |
| Animation    | GSAP + ScrollTrigger                                |
| Map          | MapLibre GL + deck.gl                               |
| Auth         | Clerk                                               |
| Web API      | Hono (mounted inside TanStack Start at `/api/*`)    |
| State/cache  | Redis                                               |
| LLM          | Fetch.AI                               |
| Agent svc    | Python FastAPI (Deepgram + Fetch.ai watchdog)       |


## Prerequisites

- Node `>=20` (uses npm; a `package-lock.json` is committed)
- Optional: Docker (for the one-command full stack), Python `3.12` (agent service)

## Quick start (frontend only)

```bash
npm install
cp .env.example .env      # optional — fill in keys to enable live services
npm run dev               # http://localhost:3000
```
## Project layout

```
src/
  routes/            # file-based routes
    index.tsx        # Page 1 — landing (3D hero)
    map/index.tsx    # Page 2 — national map
    product/index.tsx# Page 3 — live build sequence
    api/$.ts         # catch-all -> Hono web API
  components/        # landing / map / product / ui
  lib/               # redis, claude, clerk (all env-guarded)
  server/api.ts      # Hono app: the 8 API routes
  router.tsx         # TanStack Router setup (exports getRouter)
  styles/app.css     # flat design system
server.mjs           # production Node server (serves dist/)
agent-service/       # Python FastAPI: watchdog + voice
data/cache/          # pre-scraped county JSON (offline, for the map)
```

## Scripts

| Script             | Does                                   |
| ------------------ | -------------------------------------- |
| `npm run dev`      | Vite dev server (HMR) on :3000         |
| `npm run build`    | Production build to `dist/`            |
| `npm start`        | Serve the build via `server.mjs`       |
| `npm run typecheck`| `tsc --noEmit`                         |

## Production build (no Docker)

```bash
npm run build
npm start                 # http://localhost:3000 (set PORT to change)
```

## Full stack with Docker (web + agent + Redis)

```bash
cp .env.example .env     
docker compose up --build
# web   -> http://localhost:3000
# agent -> http://localhost:8000
# redis -> localhost:6379
```

## Agent service

Runs independently — see [`agent-service/README.md`](agent-service/README.md).



## Detected evidence (automated analysis)

Indexed codebase: 90 recognized source files, 645 KB.
- Anthropic (technology) — detected in the code
- CSS (language) — detected in the code
- FastAPI (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Redis (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- JavaScript (language) — claimed on Devpost, not found in the code
- AI coding agent: Claude Code — evidence: config files committed to the repository
- AI coding agent: Cursor — evidence: commit authorship or trailers

## Codebase structure (from repository index)

### Files (102 of 102)

```
.claude/launch.json
.gitignore
agent-service/.env.example
agent-service/agents/__init__.py
agent-service/agents/_resolve.py
agent-service/agents/dispatcher.py
agent-service/agents/readmes/Constructa_Environment.md
agent-service/agents/readmes/Constructa_Hazards.md
agent-service/agents/readmes/Constructa_Land_Cost.md
agent-service/agents/readmes/Constructa_Local_Dev.md
agent-service/agents/readmes/Constructa_Permits.md
agent-service/agents/readmes/Constructa_Zoning.md
agent-service/agents/send_pings.py
agent-service/Dockerfile
agent-service/main.py
agent-service/requirements.txt
agent-service/voice/__init__.py
agent-service/voice/pipeline.py
agent-service/watchdog/__init__.py
agent-service/watchdog/agent.py
data/cache/.gitkeep
data/partners.seed.json
docker-compose.yml
Dockerfile
docs/MAP_SYSTEM.md
package.json
README.md
scripts/seed-partners.mjs
server.mjs
src/components/landing/ComplianceCards.tsx
src/components/landing/ComplianceExplorer.tsx
src/components/landing/DemoBuildCanvas.tsx
src/components/landing/DemoSouthwestMap.tsx
src/components/landing/HeroCanvas.tsx
src/components/landing/InteractiveMiniDemo.tsx
src/components/landing/LandingScrollReveal.tsx
src/components/landing/MapIntelFrame.tsx
src/components/landing/USMapTeaser.tsx
src/components/map/ColorScaleLegend.tsx
src/components/map/DataCenterToggle.tsx
src/components/map/DeepDiveResearchPanel.tsx
src/components/map/FactorScale.tsx
src/components/map/FloatingActionDrawer.tsx
src/components/map/LayerSwitcher.tsx
src/components/map/LocalPartnersPanel.tsx
src/components/map/MapCanvas.tsx
src/components/map/MapViewport.tsx
src/components/map/PlotCarousel.tsx
src/components/product/AgentDock.tsx
src/components/product/CompletedWork.tsx
src/components/product/ComplianceCards.tsx
src/components/product/HorizontalTimeline.tsx
src/components/product/ModelViewer.tsx
src/components/product/PermitFormsPanel.tsx
src/components/product/ProjectConsole.tsx
src/components/product/ProjectTimeline.tsx
src/components/product/PropertyInsightCards.tsx
src/components/product/StageWorkflowDemo.tsx
src/components/ui/HudFrame.tsx
src/components/ui/SectionDivider.tsx
src/components/ui/SiteNav.tsx
src/lib/arize.ts
src/lib/asi.ts
src/lib/browserbase.ts
src/lib/browserbaseCore.ts
src/lib/cdBoundaries.ts
src/lib/claude.ts
src/lib/clerk.tsx
src/lib/compression.ts
src/lib/demoSouthwestProjection.ts
src/lib/executionPlan.ts
src/lib/geo.ts
src/lib/imageProvenance.ts
src/lib/imageScraper.ts
src/lib/imageVerification.ts
src/lib/mapClient.ts
src/lib/mapData.ts
src/lib/mapGeo.ts
src/lib/mapScores.ts
src/lib/modelGen.ts
src/lib/modelScaffold.ts
src/lib/partnerScraper.ts
src/lib/pdfCompliance.ts
src/lib/pdfFormFiller.ts
src/lib/planTypes.ts
src/lib/projectClient.ts
src/lib/projectData.ts
src/lib/redis.ts
src/lib/regionContext.ts
src/lib/stateFips.ts
src/lib/useDictation.ts
src/lib/usMapProjection.ts
src/router.tsx
src/routes/__root.tsx
src/routes/api/$.ts
src/routes/index.tsx
src/routes/map/index.tsx
src/routes/product/index.tsx
src/server/api.ts
src/styles/app.css
tsconfig.json
vite.config.ts
```

### Dependencies

- agent-service/requirements.txt: fastapi@==0.115.6, httpx@==0.28.1, pypdf@>=4.0.0, python-dotenv@==1.1.0, redis@==5.2.1, uagents@>=0.22.0, uvicorn[standard]@==0.34.0
- package.json: @anthropic-ai/sdk@^0.105.0, @browserbasehq/sdk@^2.14.1, @clerk/tanstack-react-start@^1.4.6, @deck.gl/aggregation-layers@^9.3.4, @deck.gl/core@^9.3.4, @deck.gl/layers@^9.3.4, @deck.gl/react@^9.3.4, @gsap/react@^2.1.2, @hono/node-server@^2.0.5, @react-three/drei@^10.7.7, @react-three/fiber@^9.6.1, @tailwindcss/vite@^4.1.11, @tanstack/react-query@^5.101.0, @tanstack/react-router@^1.170.16, @tanstack/react-start@^1.168.26, @types/d3-geo@^3.1.0, @types/node@^22.10.0, @types/react@^19.2.0, @types/react-dom@^19.2.0, @types/three@^0.184.0, @types/topojson-client@^3.1.5, @vitejs/plugin-react@^6.0.2, d3-geo@^3.1.1, gsap@^3.15.0, hono@^4.12.26, ioredis@^5.4.2, lucide-react@^0.525.0, maplibre-gl@^5.24.0, pdf-lib@^1.17.1, playwright-core@^1.61.0, react@^19.2.0, react-dom@^19.2.0, tailwindcss@^4.1.11, three@^0.184.0, topojson-client@^3.1.0, typescript@^5.7.0, vite@^8.0.16

### Recent commits (newest first)

- Added Devpost
- Read Me updated
- Readme
- fix: direct browser-to-agent dispatch + CORS wildcard + dotenv loading
- fix(agentverse): dispatch agents from API routes, not inside liveMode gate
- fix: resolve Agentverse integration & API connectivity issues
- feat(ui): homepage cleanup and map Quick-Score vs in-depth scoring
- Merge pull request #1 from vynx1/testing
- Merge main into testing and resolve api.ts conflict.
- Resolve api.ts merge conflict by keeping main API and contractor seed fallback.
- feat(partners): add seed file fallback for offline partner data
- feat(forms): in-process PDF auto-fill for DOSH 41-1 and Construction RFI
- Small CSS Changes
- Added Stage 4 Updates
- visualization basic functionality
- Current Workable Version
- Revert "Merge feature/ui-and-compliance-pdf: Quick Solutions, dark intake, compliance PDF pipeline"
- Revert "fix(ui): center Generate button, fix parcel colors, increase agent button padding"
- fix(ui): center Generate button, fix parcel colors, increase agent button padding
- Merge feature/ui-and-compliance-pdf: Quick Solutions, dark intake, compliance PDF pipeline

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

### docs/MAP_SYSTEM.md

```markdown
# Constructa — Interactive Map & Deep-Dive Workspace

This document covers the **map system**: the interactive choropleth, the
automated deep-dive scraping pipeline, the ASI:One multi-agent routing engine,
and — importantly — **how to set up every API and where each environment key
goes**. It implements the revised master plan and folds back in the BUILD_PLAN
design pieces that the master plan left implicit (see §7).

> **Token Company removed.** Per the revised spec, compressed Browserbase output
> pipes **directly** into ASI:One. The compression now happens locally, in
> `src/lib/compression.ts` — no third-party middleware hop.

---

## 1. The end-to-end flow

| Phase | Trigger | Frontend | Backend | Redis key |
|---|---|---|---|---|
| 1. Regional heatmap | Map mounts / hover state | `MapViewport` renders US states, color-pops on hover | `GET /api/map/states` serves state scores | `map:state:{code}` |
| 2. State focus | Click a state | Fit to state bounds; draw **congressional-district** dividing lines + a dense zip-level heatmap clipped to the outline | `GET /api/map/state/:code/regions`, `/state/:code/heatmap` | `map:state:{code}:regions` / `:cells` |
| 3. Region select + deep-dive | Click a district → "Explore This Area" | Async mutation + smooth-scroll to panel | `POST /api/map/region/:regionId/deep-dive` → Browserbase listings + scrape | `map:region:{id}:listings`, `session:{id}:raw_scrapes` |
| 4. Agent consensus | Panel lands in view | Carousel + factor scale fill in | ASI:One builds the factor-scored buy/hold/avoid guide | `cache:asi:guide:{hash}` |
| 5. Save / transition | "Save plot" / "Initiate Construction Build" | Like → Redis; CTA stashes region, routes to `/product` | Liked store + project bootstrap | `liked:plots`, `project:{new_id}` |

### Code map

```
src/lib/mapData.ts        # seeded state/district/grid/guide cache (frozen records)
src/lib/mapClient.ts      # client fetchers + score→color ramp
src/lib/compression.ts    # local semantic compression (replaces Token Company)
src/lib/browserbase.ts    # §3B headless scrape pipeline (env-guarded)
src/lib/asi.ts            # §3C ASI:One client + Agentverse fallback + Arize logs
src/lib/arize.ts          # observability logger / fallback trigger
src/server/api.ts         # /api/map/* routes incl. the stage-safe deep-dive bridge

src/components/map/MapViewport.tsx            # §2A multi-level choropleth
src/components/map/FloatingActionDrawer.tsx   # §2A "Explore This Area" CTA
src/components/map/DeepDiveResearchPanel.tsx  # §2B properties + insights
src/routes/map/index.tsx                      # composes the two + scroll/mutation
```

---

## 2. Running it with zero keys

```bash
npm install
cp .env.example .env     # optional — leave keys blank for the mock path
npm run dev              # http://localhost:3000/map
```

With no keys: state scores, district grids, properties, and the buying guide all
serve from the seeded cache in `src/lib/mapData.ts`. The deep-dive "Live mode"
to
[truncated — 10962 more characters]
```

### agent-service/agents/readmes/Constructa_Hazards.md

```markdown
# ⚠️ ConstructaHazards

**Natural Hazards specialist** — part of the [Constructa](https://github.com/vynx1/Constructa) multi-agent California land-research network.

Screens for wildfire, flood, and seismic hazard exposure using California hazard zone designations.

---

## Agent Details

| | |
|---|---|
| **Handle** | `@Constructa-hazards` |
| **Address** | `agent1qgprdps4cvspas6en82mxw82mj8zx2ft34447scav0mxq9et46czvaqxt33` |
| **Protocol** | Chat Protocol (`uagents_core.contrib.protocols.chat`) |
| **Hosting** | Agentverse (hosted endpoint) |
| **Network** | Fetch.ai |

## What it does

When a district is selected for research in Constructa, this agent receives a
Chat-Protocol message and returns a **Natural Hazards** assessment that feeds into
the consolidated land-buying guide.

### Inputs
- District / parcel location
- APN or lat/long (optional)

### Outputs
- Fire Hazard Severity Zone
- FEMA flood zone
- Seismic / fault proximity
- Composite hazard score + rationale

## How to query it

Send a `ChatMessage` containing a `TextContent` payload to the address above.

```python
import asyncio
from datetime import datetime, timezone
from uuid import uuid4
from uagents.communication import send_sync_message
from uagents.crypto import Identity
from uagents_core.contrib.protocols.chat import ChatMessage, TextContent

# Requires AGENTVERSE_API_KEY (mailbox / write scope) in your environment.
ident = Identity.from_seed("my-sender-seed", 0)
msg = ChatMessage(
    timestamp=datetime.now(timezone.utc),
    msg_id=uuid4(),
    content=[TextContent(type="text", text="Assess Natural Hazards for the selected district.")],
)

async def main():
    resp = await send_sync_message(
        "agent1qgprdps4cvspas6en82mxw82mj8zx2ft34447scav0mxq9et46czvaqxt33", msg, sender=ident, timeout=30)
    print(resp)

asyncio.run(main())
```

A successful call returns a `ChatAcknowledgement` (with an `acknowledged_msg_id`),
confirming delivery and incrementing this agent's interaction count.

## Role in Constructa

`ConstructaZoning · ConstructaPermits · ConstructaLocalDev · ConstructaLandCost · ConstructaHazards · ConstructaEnvironment`

Each specialist returns a scored **factor** (`hazards`); the orchestrator
combines all six into a single consensus recommendation for the parcel.

---

_Built with [uAgents](https://github.com/fetchai/uAgents) · Routed via ASI:One · © Constructa_

```

### Dockerfile

```
# --- Web app (TanStack Start) -------------------------------------------------
# Multi-stage: build with full deps, run with a slim image + prod deps only.

FROM node:22-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:22-slim AS runtime
WORKDIR /app
ENV NODE_ENV=production
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
COPY server.mjs ./
EXPOSE 3000
CMD ["node", "server.mjs"]

```

### docker-compose.yml

```yaml
# Full local stack: Redis + web app + Python agent service.
# Usage:  docker compose up --build
# The web app is at http://localhost:3000, the agent service at :8000.

services:
  redis:
    image: redis:7-alpine
    ports:
      - '6379:6379'
    volumes:
      - redis-data:/data
    healthcheck:
      test: ['CMD', 'redis-cli', 'ping']
      interval: 5s
      timeout: 3s
      retries: 5

  web:
    build: .
    ports:
      - '3000:3000'
    environment:
      NODE_ENV: production
      PORT: '3000'
      REDIS_URL: redis://redis:6379
      AGENT_SERVICE_URL: http://agent:8000
      # Provide these via a .env file or your secrets manager:
      ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}
      CLERK_SECRET_KEY: ${CLERK_SECRET_KEY:-}
      VITE_CLERK_PUBLISHABLE_KEY: ${VITE_CLERK_PUBLISHABLE_KEY:-}
    depends_on:
      redis:
        condition: service_healthy

  agent:
    build: ./agent-service
    ports:
      - '8000:8000'
    environment:
      PORT: '8000'
      REDIS_URL: redis://redis:6379
      WEB_ORIGIN: http://localhost:3000
      DEEPGRAM_API_KEY: ${DEEPGRAM_API_KEY:-}
      FETCHAI_AGENT_SEED: ${FETCHAI_AGENT_SEED:-}
    depends_on:
      redis:
        condition: service_healthy

volumes:
  redis-data:

```

### package.json

```
{
  "name": "Constructa",
  "version": "0.1.0",
  "private": true,
  "type": "module",
  "description": "Constructa — AI construction foreman + land-intelligence platform",
  "scripts": {
    "dev": "vite dev --port 3000",
    "build": "vite build",
    "start": "node server.mjs",
    "typecheck": "tsc --noEmit",
    "seed:partners": "node scripts/seed-partners.mjs"
  },
  "dependencies": {
    "@anthropic-ai/sdk": "^0.105.0",
    "@browserbasehq/sdk": "^2.14.1",
    "@clerk/tanstack-react-start": "^1.4.6",
    "@deck.gl/aggregation-layers": "^9.3.4",
    "@deck.gl/core": "^9.3.4",
    "@deck.gl/layers": "^9.3.4",
    "@deck.gl/react": "^9.3.4",
    "@gsap/react": "^2.1.2",
    "@hono/node-server": "^2.0.5",
    "@react-three/drei": "^10.7.7",
    "@react-three/fiber": "^9.6.1",
    "@tailwindcss/vite": "^4.1.11",
    "@tanstack/react-query": "^5.101.0",
    "@tanstack/react-router": "^1.170.16",
    "@tanstack/react-start": "^1.168.26",
    "d3-geo": "^3.1.1",
    "gsap": "^3.15.0",
    "hono": "^4.12.26",
    "ioredis": "^5.4.2",
    "lucide-react": "^0.525.0",
    "maplibre-gl": "^5.24.0",
    "pdf-lib": "^1.17.1",
    "playwright-core": "^1.61.0",
    "react": "^19.2.0",
    "react-dom": "^19.2.0",
    "tailwindcss": "^4.1.11",
    "three": "^0.184.0",
    "topojson-client": "^3.1.0"
  },
  "devDependencies": {
    "@types/d3-geo": "^3.1.0",
    "@types/node": "^22.10.0",
    "@types/react": "^19.2.0",
    "@types/react-dom": "^19.2.0",
    "@types/three": "^0.184.0",
    "@types/topojson-client": "^3.1.5",
    "@vitejs/plugin-react": "^6.0.2",
    "typescript": "^5.7.0",
    "vite": "^8.0.16"
  },
  "engines": {
    "node": ">=20.0.0"
  }
}

```

### agent-service/requirements.txt

```
fastapi==0.115.6
uvicorn[standard]==0.34.0
redis==5.2.1
httpx==0.28.1
python-dotenv==1.1.0
uagents>=0.22.0
pypdf>=4.0.0
# deepgram-sdk==3.8.0

```

### agent-service/Dockerfile

```
# --- Agent service (FastAPI) --------------------------------------------------
FROM python:3.12-slim
WORKDIR /app
ENV PYTHONUNBUFFERED=1
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

```

### server.mjs

```
// Production server. Wraps the built TanStack Start fetch handler
// (dist/server/server.js) in a Node listener and serves the static client
// bundle (dist/client). Run after `npm run build` via `npm start`.
//
// Portable: works on any Node host (Docker, Fly, Render, Railway, a VM).
import { serve } from '@hono/node-server'
import { serveStatic } from '@hono/node-server/serve-static'
import { Hono } from 'hono'
import handler from './dist/server/server.js'

const app = new Hono()

// Serve hashed client assets and any other static files from the client build.
// serveStatic falls through (next) when a file isn't found, so SSR routes
// like /map and /product still reach the handler below.
app.use('/*', serveStatic({ root: './dist/client' }))

// Everything else -> TanStack Start SSR + the Hono /api/* routes.
app.all('*', (c) => handler.fetch(c.req.raw))

const port = Number(process.env.PORT) || 3000
serve({ fetch: app.fetch, port }, (info) => {
  console.log(`Constructa listening on http://localhost:${info.port}`)
})

```

### agent-service/main.py

```python
"""Constructa agent service (FastAPI).

Hosts the two Python-side jobs from BUILD_PLAN §6:
  - the single Fetch.ai uAgent (Compliance Watchdog)
  - the Deepgram voice pipeline (Voice Log transcription)

Shares Redis with the web API. Endpoints return mock shapes until the
Deepgram / Fetch.ai integrations are wired, so the service runs standalone.
"""
from __future__ import annotations

import os
from pathlib import Path

# Load .env BEFORE any module reads os.environ (dispatcher needs AGENTVERSE_API_KEY)
from dotenv import load_dotenv
load_dotenv(Path(__file__).parent / ".env")

from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware

from watchdog.agent import active_conditions
from voice.pipeline import transcribe_and_structure
from agents.dispatcher import dispatch_to_agents

app = FastAPI(title="Constructa Agent Service", version="0.1.0")

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)


@app.get("/health")
def health() -> dict:
    return {"ok": True, "service": "Constructa-agent-service"}


@app.get("/watchdog/{project_id}/{step}")
def watchdog(project_id: str, step: int) -> dict:
    """Compliance Watchdog (Fetch.ai uAgent): conditions active/at-risk at a step."""
    conditions, alerts = active_conditions(project_id, step)
    return {
        "projectId": project_id,
        "step": step,
        "conditions": conditions,
        "alerts": alerts,
    }


@app.post("/agents/dispatch")
async def agents_dispatch(request: Request) -> dict:
    """Deliver a real Chat-Protocol message to the 6 Agentverse specialists.

    Called whenever a district is selected for research, guaranteeing the
    hosted agents receive traffic so their interaction counters increment.
    """
    payload = await request.json()
    prompt = payload.get("prompt") or "Constructa research: evaluate the selected California district."
    return await dispatch_to_agents(prompt)


@app.post("/voice-log")
async def voice_log(request: Request) -> dict:
    """Deepgram pipeline: audio/transcript -> structured daily log."""
    payload = await request.json()
    return {"log": transcribe_and_structure(payload)}


if __name__ == "__main__":
    import uvicorn

    uvicorn.run(
        "main:app",
        host="0.0.0.0",
        port=int(os.environ.get("PORT", "8000")),
        reload=bool(os.environ.get("RELOAD")),
    )
```

### src/routes/index.tsx

```typescript
import { createFileRoute, Link } from '@tanstack/react-router'
import { ComplianceExplorer } from '~/components/landing/ComplianceExplorer'
import { HeroCanvas } from '~/components/landing/HeroCanvas'
import { InteractiveMiniDemo } from '~/components/landing/InteractiveMiniDemo'
import { LandingScrollReveal } from '~/components/landing/LandingScrollReveal'
import { MapIntelFrame } from '~/components/landing/MapIntelFrame'
import { USMapTeaser } from '~/components/landing/USMapTeaser'
import { SectionDivider } from '~/components/ui/SectionDivider'

export const Route = createFileRoute('/')({
  component: LandingPage,
})

const FEATURES = [
  {
    kicker: '01 · Find',
    title: 'Winning land, ranked',
    body: 'Overlay development pressure, zoning, permit velocity, and land cost across the country to surface where building actually pencils out.',
  },
  {
    kicker: '02 · Stake',
    title: 'Instant feasibility',
    body: 'Click any parcel to claim it and run a pre-check — surrounding aging stock, revitalization upside, and the regimes you’ll face.',
  },
  {
    kicker: '03 · Build',
    title: 'Compliance on autopilot',
    body: 'A live 10-step sequence shows exactly which conditions are active at each phase, so crews never idle and inspection windows never slip.',
  },
]

function LandingPage() {
  return (
    <main className="landing">
      <LandingScrollReveal />

      <section className="hero hero--command">
        {/* Midjourney slot: hero ambient plate — see prompt #1 */}
        <div className="mj-slot mj-slot--hero-plate" aria-hidden />

        <div className="hero__grid-overlay" aria-hidden />

        <div className="hero__visual">
          <HeroCanvas />
        </div>

        <div className="hero__scrim" aria-hidden />

        <div className="hero__inner pointer-events-none">
          <div className="hero__copy hud-glass pointer-events-auto">
            <span className="hero__eyebrow font-mono">
              <span className="hero__eyebrow-dot" aria-hidden />
              AI construction foreman
            </span>

            <h1 className="hero__title max-w-max">
              Build faster where the&nbsp;rules are hardest.
            </h1>
            <p className="hero__sub max-w-max">
              The build is the easy part. The permits never stop. Constructa's
              agents track what's due, prep your filings, and keep compliance
              moving—so work doesn't stall waiting on paperwork.
            </p>

            <div className="hero__cta">
              <Link to="/product" className="btn btn--glow">
                Get started
              </Link>
              <Link to="/map" className="btn btn--ghost-dark">
                Find your land
              </Link>
            </div>

            <div className="hero__proof font-mono">
              <span>OSHA</span>
              <span>Energy code</span>
              <span>Environmental review</span>
              <span>Building code</span>
              <span>SWPPP</span>
            </div>
          </div>
        </div>
      </section>

      <SectionDivider />

      <section className="section section--compact reveal-section">
        <p className="section__eyebrow font-mono">Why Constructa</p>
        <h2 className="section__title section__title--wide">
          The land-to-launch loop, compressed.
        </h2>
        <div className="features features--telemetry">
          {FEATURES.map((f) => (
            <article key={f.kicker} className="feature feature--telemetry">
              <span className="feature__kicker font-mono">{f.kicker}</span>
              <h3 className="feature__title">{f.title}</h3>
              <p className="feature__body">{f.body}</p>
            </article>
          ))}
        </div>
      </section>

      <SectionDivider />

      <section className="section section--map reveal-section">
        <div className="map-teaser">
          <div className="map-teaser__copy">
            <p className="section__eyebrow font-mono">National land intelligence</p>
            <h2 className="section__title section__title--wide">
              See where the next development wins.
            </h2>
            <p className="map-teaser__body">
              Every market scored on development pressure, permit velocity, and
              land cost. Click a metro in the list or on the map to pin intel —
              drag to rotate and scroll to zoom the 3D view.
            </p>
            <Link to="/map" className="btn btn--primary">
              Explore the map
            </Link>
          </div>
          <MapIntelFrame>
            <USMapTeaser />
          </MapIntelFrame>
        </div>
      </section>

      <SectionDivider />

      <section className="section section--compact section--compliance reveal-section mx-auto max-w-7xl px-6">
        {/* Midjourney slot: compliance blueprint texture — see prompt #3 */}
        <div className="mj-slot mj-slot--compliance-bg" aria-hidden />
        <p className="section__eyebrow font-mono">Coverage</p>
        <h2 className="section__title section__title--wide max-w-none">
          What we keep compliant
        </h2>
        <ComplianceExplorer />
      </section>

      <SectionDivider />

      <InteractiveMiniDemo />
    </main>
  )
}

```

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