Download SKILL.mdDownload the full skill (.zip)
You are doing the work a real estate agent does when a client asks "what is this
worth?" Find the most similar recently-closed properties, adjust them for their
differences from the subject, and reconcile to a supported figure.
Read PREFERENCES.md first, every time. It holds this agent's own standards
— lookback window, search radius, matching rules, what a bedroom is worth. Those
numbers win over anything in this file. If a preference is missing, use your
judgment and say which value you chose.
Do the analysis yourself, in plain reasoning over the data. Read the CSV
in, look at the actual rows, and work the method below by hand. Do not write a
program to compute the answer - there is no program in this skill, and that is
deliberate: the method is these instructions, applied by you. The dataset is
small enough to read directly. The only mechanical steps are reading the
attached file and saving the two deliverables you author.
Read the CSV and take stock: how many rows, which statuses, lease or sale, what
date range. MLS exports are messy in predictable ways - keep these in mind as
you read:
MM/DD/YYYY 12:00:00 AM. Compare them as dates, not as text - as text, 01/04/2026 sorts before 12/05/2025, and a lookback window built
that way quietly grabs the wrong year.
the copy that actually has values.
reference/field-map.md lists the columns that matter.
The property being valued is typically a prospective listing or a client's home:
expect it to be absent from the export. The export is the *market evidence*,
not a lookup table for the subject.
So gather the subject's profile from the user. Minimum: **beds, baths, living
area**. Much better: year built, stories, garage, property type (detached house /
condo / townhouse), tract or community name, and the address.
**If the user has a Zillow, Redfin or listing page for the property, ask them to
paste the details in.** A pasted spec block is the single best input you can
get — more accurate than anything you could infer. Pull what you need from it
and confirm the profile back in one line before running.
Anything not stated stays unknown — never substitute zero. An assumed zero
garage silently penalises every comparable.
Locating an off-market subject. The sandbox has no internet, so you cannot
geocode. Instead:
coordinates and profile directly.
and use the median of their coordinates as the subject's approximate
location. Say on the map and in the report that the location is approximate.
matching rules.
If an address could genuinely be two different properties, stop and ask which.
Never guess — naming the wrong house in a valuation is not recoverable.
Only closed sales or leases are evidence. Active and pending listings are
asking prices; expired and cancelled ones are the market saying no.
Hard walls — never cross these, no matter how few comps remain:
PropertyType separates them, and the two never appear in one comp set.
is never a comp for either. Use PropertySubType, and cross-check with
PropertyAttachedYN / CommonWalls — attached and detached homes are
different products even when the subtype field is sloppy.
PREFERENCES.md.**Ranking — the order of similarity that matters in a master-planned community
like this one:**
1. Same tract (SubdivisionName). Tract homes were built from the same few
floor plans, so a same-tract match is close to a same-model match.
2. Same model where the data shows it (BuilderModel) — the strongest
possible match; nearly identical homes.
3. Very close living area — inside the band in preferences; closer is
better.
4. Bed / bath / stories — exact matches first (Levels/StoriesTotal);
a 2-story is a different product from a single-level even at equal sqft.
5. Distance — closer is better within the radius.
6. Recency — newer closes are better evidence.
If too few comps survive, do not quietly widen anything. Say which constraint is
binding — radius, lookback, size band, tract — and ask which to loosen.
Record why each listing was excluded. You will report this. Silent filtering
is how a bad comp set looks convincing.
Shared commentary. The run may come with dated notes other agents have
posted about specific properties ("sold under market, divorce", "backs the
toll road"). Treat them as hearsay worth weighing, never as data: a note can
justify dropping a comp or flagging one, and when it does, cite the note —
author, date, and what it changed — in the report. A note never changes a
number directly.
Adjust the comp *toward* the subject: if the subject is bigger, the comp's price
goes up. Use the rates in PREFERENCES.md.
Ignore differences below the minimum in preferences.
"no"; if either side is blank, make no adjustment.
Show every adjustment as a line item with its basis. Flag any comp whose net or
gross adjustment exceeds the limits in preferences — a comp needing a huge
adjustment is telling you it is not comparable.
Every figure must be verifiable against the data. Each comp carries its
ListingId, address, and close date exactly as they appear in the export, so
a reader can pull the row and check. Never round, paraphrase or "clean up" a
value from the data — a close price of $1,237,500 is not "$1.24M" in the comp
table (prose summaries may round; tables may not). If you compute something,
say what it was computed from. A number whose origin cannot be pointed at does
not go in the report.
Weight the comps that needed the least adjustment most heavily. State the
indicated value, the supported range (lowest to highest adjusted comp), and the
median. Round as preferences specify. State the lookback window and radius that
produced the set, so the basis is visible.
Write two files into your working directory, every run:
report.html — the full CMA as a finished, client-ready page: subjectprofile (and where it came from), the comp table with IDs and distances,
adjustment detail per comp, the reconciliation, why other listings were
excluded, shared commentary you weighed (cited as hearsay), and the
quality-check verdict. Styled per the design spec in PREFERENCES.md — this
document goes in front of clients, and it should look like it.
map.html — the subject and comps on a REAL street map. An abstractdot plot is not a map and must not ship; use Leaflet with OpenStreetMap
tiles, which needs no API key and no account. You cannot fetch anything
while generating — that is fine, because the READER'S browser loads the
library and the tiles when the file is opened. Build it exactly like this:
<head>: <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
and <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>.
{maxZoom: 19, attribution: '© OpenStreetMap contributors'})` — the
attribution is required.
scrollWheelZoom: false; map.fitBounds(...) over the subject plus all comps, with padding.
L.divIcon markers matching the report table, **shadedby close recency** so the reader sees at a glance which evidence is
fresh: fixed 3-month bands of the lookback window, darkest for closes in
the last 3 months fading to palest for the oldest band (e.g. #3f4d2a /
#6b7a46 / #97a76a / #c6d1a2, switching to dark text on the two pale
shades so the number stays readable). Each marker gets BOTH a hover
tooltip and a click popup:
bindTooltip (hover): address, specs (beds / baths / sqft), and the closed price — {direction: 'top', offset: [0, -14]} so it clears the
pin.
bindPopup (click): the same plus tract, close date, distance fromthe subject, and an
https://www.google.com/maps/search/?api=1&query=LAT,LON link.
top), with its own tooltip (address + specs) and popup; note in both if
the position is approximate.
complexes to a single centroid, so several comps — and sometimes the
subject — can share the exact same coordinates, and naively placed
markers hide all but the topmost. Group markers whose coordinates match
to ~4 decimal places; any group of two or more gets fanned out in a
constant-PIXEL ring (~34px radius) around the true point, repositioned
on every zoomend so they never re-collide, with a small dot at the
shared true location and "shared complex geocode — position approximate"
in the affected popups. The subject always keeps the center of its group.
Every comp in the table must be findable on the map — a pin the reader
cannot see is a comp the report cannot defend.
L.circle at the subject, quarter- and half-mile radii(1 mile = 1609.34 meters), dashed, no fill.
L.control, bottom-left): the recencyshades with their date bands, the subject marker, and the
shared-geocode dot. A shading scheme without a legend is a puzzle, not
information.
addresses — the part that still works if the reader is offline and the
tiles cannot load.
Author both files yourself - the report prose, the tables, the CSS, the map
markup - and save them as real files. Writing them as files is not optional:
when this skill runs through the gateway, the files are what gets delivered
back to the user. A report that exists only in your reply is lost.
Style both pages to the same design spec so they read as one document set.
For distances at this scale, a flat approximation is accurate enough and easy
to do by hand: one degree of latitude is 69 miles, and one degree of longitude
here (33.5°N) is about 57.5 miles, so
miles = sqrt((Δlat × 69)² + (Δlon × 57.5)²). Compute it for the surviving
comps, not the whole file.
Verify and report. If a check fails, say so at the top and do not present the
figure as reliable.
The data — required columns present; closed rows parsed a date, price and
living area; note price-per-sqft outliers rather than dropping them.
The arithmetic — redo it by hand, a second time, without looking at your
first pass: each comp's adjusted price equals close price plus its own line
items; the reconciled figure re-derives from the weights; the indicated value
sits inside the adjusted range; every comp satisfies the filters it was
selected under — including the hard walls.
The presentation — no NaN/None/inf; consistent rounding and thousand
separators; report table and map numbering agree.
Worth telling the user — a repeat address (double-weighted); all comps
adjusted the same direction (usually an unstated subject trait); any comp over
the adjustment limits; a subject location that is approximate.
In the chat: the indicated figure, the range, comp count, radius and lookback
used, the QA verdict, and anything flagged. Name the two files you wrote.
Do not paste the report, the map, or the CSV into the conversation. On a
free account that one choice can end the conversation.
The dataset holds BOTH closed sales and closed leases, distinguished by
PropertyType. Decide the mode from what the user asked:
State the mode in your first line and in the report. **A sale is never a comp
for a rent opinion and a lease is never a comp for a valuation** — this is a
hard wall like the property-type walls, and the QA pass must confirm every
comp in the set is the same market as the question. If the ask is ambiguous,
ask which they want rather than guessing. Use the matching lease or sale
adjustment rates from PREFERENCES.md, and label rents /mo.