Case study

Manitoba Cottage Search

A web application and listing-ingestion workflow for tracking, scoring, and reviewing Manitoba cottage listings. The system combines FastAPI, SQLite, HTMX, a structured intake API, and agent-assisted discovery so listing data can move into a review app with provenance instead of living in an untraceable spreadsheet.

Case study at a glance

This project is useful because it turns a messy monitoring workflow into a structured application with explicit provenance, clearer review decisions, and better boundaries between automation and storage.

Built Structured intake and listing history

The app already has the ingestion spine: listings, scores, notes, status history, and import audit tables.

In progress Review tracking and extractor hardening

The strongest remaining work is dedicated review events and deterministic saved-search extraction.

Learned Import, review, and status should stay separate

That separation makes the system auditable and keeps rejected listings from disappearing.

The data was stuck in a spreadsheet

Cottage search workflows create a slow stream of new candidates, partial duplicates, source-specific gaps, and repeated review decisions. A spreadsheet can hold rows, but it cannot cleanly answer basic operational questions such as how a listing entered the system, whether it was reviewed, why it was rejected, or which source fields were strongest.

The goal was to move that workflow into a real application with a dedicated intake contract and a reviewable history. If an agent is going to support the process over time, it needs structured access and predictable write paths instead of browser-only manipulation or direct database edits.

What exists now and how the pipeline is shaped

The current system is a SQL-backed web app with a structured intake path. The app owns the canonical listing model, while agent-driven discovery stays outside the storage layer and posts into the same API used for normalized imports.

The implemented spine already includes an authenticated POST /api/imports/intake path, listing records, score records, notes, status history, and import audit tables. That means the app can already explain how data entered the system and what changed afterward.

The architectural split is the important part: import, review, and status are treated as different concepts. The development work now focuses on making review outcomes first-class so rejected candidates are still searchable and auditable instead of disappearing into notes or never being recorded at all.

Deduplication is planned around a practical hierarchy: MLS number first, then source URL, then normalized address and locality.

AI role: discovery, scoring, and bounded automation

The agent side is intentionally narrow. It is responsible for discovery, extraction, scoring, and packaging data for intake — not for bypassing the application model.

Discovery Saved-search extraction

The watcher uses a Realtor.ca saved search as the discovery source instead of relying on prompt-only browsing.

Boundary API-first persistence

Everything still goes through the same authenticated intake path so provenance and normalization stay consistent.

Failure handling Explicit error states

The extractor design calls for visible blockers and age-gate outcomes rather than silent zero-result runs.

What I learned

The key lesson is not about real estate data. It is about workflow design: once the application exposes a structured, authenticated write path, automation becomes safer to layer on top.

01

The intake API is the linchpin.

A stable write contract makes browser automation and scoring additive instead of dangerous.

02

Auditability depends on separation.

Import, review, and status look similar in a sheet, but they create better software when modeled separately.

03

Failure states should be explicit.

“No listings found” is not enough. Good automation says whether the source was empty, blocked, stale, or broken.

04

Human review is still part of the product.

The point is not blind end-to-end automation. The point is better candidate intake and better decisions.