This root
SKILL.mdis the skill’s entry point and the orchestrator. It routes to the domain subagents inagents/(each is a folder with its ownSKILL.md) and coordinates the buyer journey. See Agent Architecture & Buyer Journey. A skill is defined by aSKILL.mdat its folder root — the orchestrator’s folder is this top-level skill folder, exactly as each subagent’sSKILL.mdsits at its ownagents/<name>-advisor/root.
Turn any bundle of property documents (seller disclosures, surveys, inspection reports, receipts, HOA docs, title/appraisal, listing sheets, photos) into a professional, decision-grade Property Investigation Report — covering condition, risks, benefits, comparative value, financials, and a resale outlook.
The skill is incremental: point it at a folder of documents and it produces a report. Drop in more documents later and it regenerates an updated report with a change log, without redoing everything from scratch.
This skill produces analysis for decision support. It is not legal, financial, or professional inspection advice. Every report must tell the buyer to verify material items with the appropriate licensed professional.
app/RESPONSIBLE-AI.md.templates/report-template.html (Property Investigation, Loan Comparison, Neighborhood, and multi-property Comparison reports). Keep it dependency-free so it opens/prints anywhere. Markdown is now optional/secondary — only produce a .md copy if the user asks. Interactive dashboards (loan/neighborhood) are still generated as their own HTML in addition to the report.The runnable prototype is provider-neutral and local-first. It may use GitHub Models, OpenAI, or Azure OpenAI when explicitly configured, and otherwise uses the deterministic grounded analyzer. Azure OpenAI is the production target, not a claim that one model is universally best. It is selected because model access can share the solution’s Azure identity, private-network, regional, governance, safety, and monitoring boundary and integrate with Azure AI Search, Content Safety, Purview, Key Vault, and managed identity. Keep the provider adapter so candidate model/deployment versions can be evaluated and replaced without rewriting the agent workflow.
For production retrieval, store approved property documents and licensed/public records in an
engagement-scoped Blob container, extract text/tables/pages, and index only approved chunks in Azure AI Search.
Every chunk must carry engagementId, propertyId, documentId, document type, page/record locator, source
authority, signed/effective date, jurisdiction, and retrieval timestamp. Every specialist query must:
unknown / verify when retrieved evidence is insufficient or contradictory.Do not apply “newest wins” across unlike sources. Use this authority order and preserve every material discrepancy:
If equally authoritative sources still conflict, retain both values, set the field to disputed/unverified, reduce confidence, explain the decision impact, and name the professional or official source that must resolve it. Never silently average conflicting facts.
Evaluate each model, prompt, retrieval configuration, and specialist against versioned, de-identified golden
property packets. Record extraction exact-match/F1 for critical fields; citation precision and material-claim
coverage; groundedness/unsupported-claim rate; conflict-resolution and abstention accuracy; schema and
calculation validity; decision consistency; cross-property leakage; prompt-injection resistance; and licensed
expert agreement on high-impact findings. Release thresholds and accountable owners must be approved before
customer deployment; failed gates block promotion. See app/RESPONSIBLE-AI.md.
This skill is an orchestrator over five domain subagents in agents/. Each subagent runs
independently (by its own trigger phrases) or is invoked by the orchestrator at the right stage of a
purchase. Full design: docs/AGENT-ARCHITECTURE-DESIGN.md. User-facing
walkthrough of every agent’s inputs/outputs: docs/How-To-Use-Guide.html.
Per-agent index: agents/README.md.
| Subagent | Folder | Leads stage | Independent triggers (examples) |
|---|---|---|---|
| Neighborhood Advisor | agents/neighborhood-advisor/ |
Neighborhood & value | “comps”, “what’s this home worth”, “neighbors’ details” |
| Comparison Advisor | agents/comparison-advisor/ |
Choosing among homes | “which should I buy”, “best buy”, “A vs B” |
| Loan Advisor | agents/loan-advisor/ |
Financing | “compare loans”, “review my Loan Estimate”, “should I pay points” |
| Inspection Advisor | agents/inspection-advisor/ |
Option period | “review my inspection”, “estimate repairs”, “should I walk away” |
| Insurance Advisor | agents/insurance-advisor/ |
Insurance (pre-closing) | “compare home insurance”, “insurance call prep” |
Routing: match the request to a subagent by trigger phrase first. If a property journey is active and the request is ambiguous, pick the stage-appropriate subagent. The orchestrator itself owns Discovery, Investigation, the main report, the contract & deadlines, and closing/move-in.
Why specialists instead of one general agent: each domain has different evidence, calculations, schemas, guardrails, and professional escalation paths. Specialists keep prompts focused, activate only when their inputs exist, can run and be evaluated independently, and limit the blast radius of a prompt/model change. They are logical skill modules, not a claim that six unconstrained model calls always run in parallel.
Coordination loop: route -> retrieve property-scoped evidence -> run specialist -> validate structured
result -> update only the owned state section -> reconcile dependent values -> present one next action.
GitHub Copilot invocation. The orchestrator and the five advisors are packaged as discoverable Copilot
custom agents in .github/agents/ (property-investigator.agent.md plus one
*-advisor.agent.md per specialist), with a slash-command entry point in
.github/prompts/property-report.prompt.md. Each advisor agent is
thin: it reads its canonical agents/<name>-advisor/SKILL.md (the source of truth) and produces that advisor’s
reports. The orchestrator’s agents: list restricts delegation to exactly those five named advisors.
Report-run manifest (proof that “all reports” were produced). At the end of every run the orchestrator
writes .property/report-run.json (schema:
templates/report-run-schema.json) accounting for every deliverable as
created, updated, unchanged, not_applicable, or blocked (with a reason). Never claim all reports were
created when a conditional report lacked its inputs — report it as blocked/not applicable instead.
Buyer journey (checklist with dependencies, not a rigid wizard):
0 Readiness → 1 Discovery → 2 Investigation → 3 Neighborhood & value → 4 Financing → 5 Offer & contract →
6 Option period (inspection) → 7 Insurance → 8 Closing prep → 9 Post-closing.
First-time-buyer entry point. Triggers like “help me buy a house”, “start buying a home”, “I’m a first-time buyer”, “where do I start” launch the guided journey. For a first-timer, do the following before diving into documents:
.property/journey.jsonOne per property; the “spine” every subagent reads for context and writes its results back to. It never
contains another property’s data (Principle 2). The authoritative, validated schema is
templates/journey-state-schema.json — populate/validate against it.
Shape:
{
"property": { "address": "…", "slug": "…", "reportPrefix": "…",
"purchasePrice": 0, "loanAmount": null, "replacementCost": null, "county": "…" },
"stage": "option-period",
"firstTimeBuyer": false,
"contract": { "effectiveDate": null, "optionDeadline": null, "financingApprovalDate": null,
"closingDate": null, "maxRate": null, "financingType": null,
"asIs": true, "status": "verified|unverified" },
"value": { "ran": false, "estimatedValue": null, "estimatedValueBasis": null, "cushion": null },
"loan": { "ran": false, "bestLender": null, "rate": null, "monthlyPITI": null,
"escrowMonthly": null, "cashToClose": null, "maxRateFlag": null },
"insurance": { "ran": false, "carrier": null, "annualPremium": null,
"roofSettlement": null, "deductibles": null },
"inspection": { "ran": false, "exposureBase": null, "sellerScope": null,
"buyerCredit": null, "priceReduction": null, "dealBreaker": "no|conditional|yes|pending" },
"openRisks": [],
"updated": "ISO-8601",
"versions": []
}
Rules: every write is timestamped and appends a versions entry; a subagent only writes its own section +
openRisks; the orchestrator is the sole writer of stage and contract; missing values stay null (never invented).
The domains are financially coupled — after any subagent changes numbers, reconcile automatically and keep a new timestamped version (do not silently discard the prior one):
insurance.annualPremium changes and loan.ran = true, auto-refresh the
loan escrow, PITI, and cash-to-close, regenerate the loan report/dashboard, and archive the prior HTML to
loan/history/ with a yyyy-MM-dd_HHmm stamp. Tell the user to give the lender the final premium.inspection.buyerCredit (seller credit) or a price reduction
changes, auto-refresh cash-to-close (credits are lender-capped; a price cut changes the loan amount)
and re-flag the best pick, archiving the prior version.Top-level layout of the skill repo:
Property-Report-Skill/
SKILL.md # ORCHESTRATOR + skill entry point (routing, journey, reconciliation)
README.md # repo overview & quick start
agents/ # domain SUBAGENTS (independently triggerable; invoked by the orchestrator)
README.md # index of the orchestrator + 5 advisors
neighborhood-advisor/SKILL.md # comps / appraisal-district value
comparison-advisor/SKILL.md # multi-property best-buy pick
loan-advisor/SKILL.md # loan/mortgage comparison + pre-approval
inspection-advisor/SKILL.md # inspection exposure + repair budget
insurance-advisor/SKILL.md # insurance call-prep + comparison
templates/ # report/dashboard scaffolds + data schemas
scripts/ # extractor + helper scripts (extract_documents.py, process_narration.py)
docs/ # design & user docs (AGENT-ARCHITECTURE-DESIGN.md, How-To-Use-Guide.html)
demo/ # standalone interactive HTML demos (cockpit + mobile app)
assets/ # cover art, video, and presentation/ (deck source + built PPTX)
app/ # runnable reference web app
properties/ # local-only property workspaces plus best-buy comparisons
All properties live under the skill’s properties/ directory, one folder per property
(named after the address, e.g. 123-Example-St-Sample-City-ST). Recommended per-property layout
(auto-created if missing):
Property-Report-Skill/
properties/
<Address-Slug>/
documents/ # raw inputs (PDF/DOCX/JPG/PNG). User drops files here.
data/ # structured outputs (CSV/JSON) for reuse & year-over-year diffs
loan/ # OPTIONAL loan-shopping workspace (Phase 9)
<Lender Name>/ # one subfolder per lender; drop that lender's quote PDFs here (e.g. First Lending/, Chase/)
data/loan-quotes.json # normalized quotes (one record per lender+rate option)
history/ # timestamped prior HTML report versions
Loan-Comparison-Report.html # the deliverable (HTML by default — Phase 9)
loan-comparison-dashboard.html # interactive side-by-side (from template)
insurance/ # OPTIONAL home-insurance workspace (Phase 10)
<Company Name>/ # one subfolder per carrier; drop that carrier's quote PDF(s) here
Insurance-Call-Prep.html # Q&A sheet (from insurance-call-prep-template.html)
Insurance-Comparison-Report.html # data-driven cross-carrier dashboard (from insurance-comparison-dashboard-template.html, when ≥2 quotes)
inspection/ # OPTIONAL inspection workspace (Phase 11)
documents/ # inspection reports/photos; user drops files here
estimates/ # specialist evaluations + contractor quotes
report-page-images/ # rendered report pages (page-NN.jpg) for per-finding evidence
data/inspection-findings.json # normalized findings, estimates, and decision record
data/inspection-evidence.js # per-finding inspector text + page image (from template)
data/diy-guidance.js # DIY steps, direct guides, material kits (from template)
Inspection-Readiness-Checklist.html # pre-inspection preparation
Inspection-Analysis-Report.html # major/minor findings, cost ranges, negotiation, decision
Inspection-Repair-Budget.html # interactive budget tool: DIY/specialist plans, filters, evidence, shopping-list export
Inspection-Repair-Request.html # agent-ready prioritized request; generated only after analysis
history/ # timestamped prior HTML versions
.property/
manifest.json # tracked docs: name, hash, type, processed date, key facts
journey.json # shared journey state spine (stage, contract dates, loan/insurance/inspection results)
extracted/ # per-document extracted text (cache)
render/ # rendered PNGs of scanned pages
Property-Investigation-Report.html # the main deliverable (HTML by default — Phase 6)
Neighborhood-Comparison-Report.html # appraisal-district comp report (Phase 4.5; required on online first run)
neighborhood-dashboard.html # interactive comp dashboard (Phase 4.5; required on online first run)
Report file naming (required, all properties). Prefix every generated report/dashboard HTML filename
with a short property name — the subdivision/community name from the legal description (e.g.
Lakewood-Hills-West-), or a distinctive house name if the user gives one — not the full street address.
So the deliverables become e.g. Lakewood-Hills-West-Property-Investigation-Report.html,
Lakewood-Hills-West-Neighborhood-Comparison-Report.html, Lakewood-Hills-West-neighborhood-dashboard.html
(and the loan/insurance/inspection reports likewise). Keep the data/ CSV/JSON and .property/ files unprefixed.
When you prefix or rename, update every internal link and the manifest.json outputs so nothing breaks.
Name the property folder from the address (e.g. 123-Example-St-Sample-City-ST).
If the user’s documents are loose in the property folder (not in documents/), treat the
whole folder as the document source — do not force a move unless asked.
.pdf .docx .doc .jpg .jpeg .png .txt .md .xlsx)..property/manifest.json if present. Compute a hash (or size+mtime) per file.For each NEW/CHANGED file, run the extractor (see scripts/extract_documents.py):
.property/extracted/<file>.txt and record key facts in the manifest.Classify each document into a type:
seller_disclosure | disclosure_update | survey | title_affidavit | inspection | roof/repair_receipt | hoa | appraisal | listing_sheet | photos | permit | other.
Populate a normalized record (see templates/property-data-schema.json). Capture at least:
When two documents conflict (e.g., an original disclosure vs. an update), the most recent signed/dated document wins; note the discrepancy explicitly.
If online access is available, enrich (and cite) with:
→ Delegate to the Neighborhood Advisor subagent (agents/neighborhood-advisor/SKILL.md) automatically
during every initial property investigation when public web/CAD access is available. Also delegate whenever
the user explicitly wants comps / neighborhood value / “the neighbors’ details,” or during the journey’s
value stage. Before the initial investigation is complete, the advisor must write
Neighborhood-Comparison-Report.html, neighborhood-dashboard.html, and reusable CSV/JSON in data/;
update journey.json.value and the manifest; and return a concise summary for the main report with links to
the neighborhood files. If closed MLS sales are unavailable, label the result a CAD mass-appraisal
benchmark, not a closed-comp appraisal or sale-value estimate.
Skip this automatic delegation only when the user explicitly opts out or online/CAD data is unavailable or blocked. In that case, the main report and manifest must say that neighborhood outputs were not generated, give the reason, and retain neighborhood value as an open item. Property-only (Principle 7).
Report analyses (orchestrator — used in the main report, Phase 6). Produce these, each with severity + verification actions:
yes, conditional yes, or no; state the intended holding period, resale-demand drivers, marketability risks, nearby active competition, and the price/fact/condition that would change the verdict. Use closed comps when available; clearly separate active asking prices, CAD values, and forecasts from sold evidence. Do not hide behind a generic outlook.high, medium, or low confidence. Give a respectful traditional Vastu read of that facing. If the direction cannot be established, say unverified and make survey/compass verification a prominent open item; never guess. Framing (required): state clearly that Vastu Shastra is a traditional/cultural belief system, not science or an appraisal input, must not override the practical/financial findings, and that a room-level reading needs a Vastu consultant on-site with a compass (reference resource: subhavaastu.com). See the facing-determination method in Tooling Notes.🟢 Highly Recommended, 🟡 Medium / with caution, or 🔴 Low / not recommended.
UNCONFIRMED — verify and set the rating to
PENDING; never guess a builder, and never rate a builder you haven’t confirmed.The orchestrator owns the executed contract and every deadline that flows from it. Run this whenever the user shares a purchase contract/addendum, asks about deadlines, or drops a signed form to validate (e.g. a TREC Notice of Buyer’s Termination of Contract, amendment, or addendum). This is decision-support issue-spotting, not legal advice — always tell the buyer to confirm timing and delivery with their agent/attorney.
journey.json.contract (orchestrator is the sole writer): Effective
Date, option-period days + the exact expiration date & time (5:00 p.m. local time rule), financing/
Buyer-Approval deadline, closing date, as-is/repair terms, earnest-money & option-fee amounts and
whether/when each was actually delivered, escrow agent, and the full names of all buyers and sellers.
Missing/conflicting values stay null and set contract.status = unverified (make that the first 🔴).Render Property-Investigation-Report.html from templates/report-template.html (HTML is the default output — Principle 9). Requirements:
<main id="content">.<span class="sev r|y|g"> chips; verification actions; key cautions in .callout boxes..md copy only if the user asks.When re-run after new documents are added:
manifest.json with the newly processed files.If purchase price or user-provided context changes (e.g., “I’m buying at $X”), treat that as an input and refresh Comparative Value, Financials, and Outlook.
→ Delegate to the Comparison Advisor subagent (agents/comparison-advisor/SKILL.md) when the user asks to
compare properties / pick the best buy / rank homes. It normalizes each property from its own folder
(read-only, no cross-mixing), scores on weighted resale-tilted criteria, and writes one best-buy pick + switch
condition to a properties/<A>-vs-<B>-Best-Buy-YYYY-MM-DD.html file in the parent properties/ folder
(outside every individual property folder).
→ Delegate to the Loan Advisor subagent (agents/loan-advisor/SKILL.md) when the user wants to compare
loan/mortgage quotes or get pre-approved. It extracts each lender’s quote, anchors to the contract’s max
rate / closing date, produces Loan-Comparison-Report.html + the interactive loan-comparison-dashboard.html,
and records loan.* in journey.json. Financial estimate analysis, not advice. Orchestrator note: after
insurance/inspection change numbers, the loan is auto-refreshed (see Cross-agent reconciliation).
→ Delegate to the Insurance Advisor subagent (agents/insurance-advisor/SKILL.md) when the user shares an
insurance quote or asks about insuring the home. It maintains Insurance-Call-Prep.html, builds the
data-driven Insurance-Comparison-Report.html for ≥2 carriers, and records insurance.annualPremium etc. in
journey.json. Orchestrator note: a new premium triggers the loan escrow auto-refresh.
→ Delegate to the Inspection Advisor subagent (agents/inspection-advisor/SKILL.md) when the user adds an
inspection report, specialist report, photos, or contractor estimates. It locks the executed-contract option
deadline, classifies findings by consequence, and always generates the interactive Inspection-Repair-Budget.html
whenever an inspection report is present (this is mandatory, never skipped, never gated on the user asking)
plus Inspection-Analysis-Report.html / Inspection-Repair-Request.html, recording inspection.* (including
any buyerCredit) in journey.json. Orchestrator note: a seller credit / price change triggers the loan
cash-to-close auto-refresh.
scripts/extract_documents.py (pdfplumber + python-docx + PyMuPDF render fallback). Install on demand: pip install pdfplumber python-docx PyMuPDF openpyxl.get_pixmap(matrix=fitz.Matrix(3,3))) and, if needed, crop the signature band and zoom, then read it visually to confirm which box is ticked and whether each signature/date block is actually completed. Do not treat a “Verified” header watermark as a completed signature. Clean up the temporary render afterward unless asked to keep it.for (const pid of ids) {
await page.goto(`${DETAIL_BASE}/${pid}/${year}`, {waitUntil:'networkidle'});
for (const name of ['Values','Improvement'])
try { await page.locator(`button:has-text("${name}")`).first().click({timeout:2500}); } catch(e){}
const rec = await page.evaluate(() => { /* scrape address, legal, owner, Main Area sqft, Year Built, 2026 Net Appraised */ });
out.push({pid, ...rec});
}
Adapt selectors/labels to the specific CAD portal. Set a generous timeoutMs and batch to avoid timeouts.
templates/neighborhood-dashboard-template.html, replace the DATA array + subject/price constants, keep the privacy footer.templates/loan-comparison-dashboard-template.html, set the CONFIG block (price, planned loan, contract maxRate, closing date, holding years) + the QUOTES array (one object per lender), keep the disclaimer footer. The page auto-derives PITI, points break-even, net holding-period cost, and the contract-max-rate / lock-expiry flags. For amounts a quote doesn’t state, use null (renders “—”) rather than guessing. Loan-quote figures are lender estimates — never present them as locked or as financial advice.templates/insurance-comparison-dashboard-template.html to insurance/Insurance-Comparison-Report.html and fill CONFIG + the CARRIERS array + the HOME_ROWS/AUTO_ROWS/GAP_ROWS/SOURCES arrays (table columns and card totals are generated from the data, so 2+ carriers and home-only or home+auto both render, with tier/billing toggles and an editable unknown-fee input). Copy templates/insurance-call-prep-template.html to insurance/Insurance-Call-Prep.html and fill its ``. Use only document-traceable premiums; rebuild fresh each run; dwelling = replacement cost, not price; flag roof RCV vs ACV. Insurance figures are quotes — not insurance or financial advice.templates/inspection-repair-budget-template.html to inspection/Inspection-Repair-Budget.html, and its two data companions templates/inspection-evidence-template.js → inspection/data/inspection-evidence.js and templates/diy-guidance-template.js → inspection/data/diy-guidance.js. Fill the `` in the HTML header and the DATA arrays (ORIGINAL_ITEMS, HANDLING_BY_ITEM, MINIMUM_BUDGET_COSTS, SPECIALIST_BY_ITEM, TIMING_BY_ITEM, INSURANCE_GROUPS, SPECIALIST_BUNDLES, BUNDLE_CATEGORY_LABELS); render each cited report page to inspection/report-page-images/page-NN.jpg. The engine (filters, presets, evidence expanders, DIY guides, retailer links, CSV + shopping-list export) is property-agnostic — only tokens and data change. Keep DIY only for homeowner-safe work with a stop condition; route roof/structural/gas/energized-electrical/garage-spring/concealed-leak/pressure/HVAC-control/high-access work to a specialist.https://hazards.fema.gov/arcgis/rest/services/public/NFHL/MapServer/28/query?geometry=<lon>,<lat>&geometryType=esriGeometryPoint&inSR=4326&spatialRel=esriSpatialRelIntersects&outFields=FLD_ZONE,SFHA_TF&returnGeometry=false&f=json
(fetch same-origin from a hazards.fema.gov page — direct file-origin fetch is CORS-blocked; the msc.fema.gov search page only renders the zone in an interactive map). FLD_ZONE=X with SFHA_TF=F = not in a Special Flood Hazard Area (no mandated flood insurance). State the zone as awareness; the binding determination is still the lender’s (a human-lane task). Get each property’s lat/long from the listing/map link.properties/ folder (not a property subfolder), properties/Pre-Contract-Loss-Avoidance-Checklist.html. Purpose: minimize sunk loss (option fee + inspection) by gating money against deal-killers knowable for $0 from the seller’s disclosure + MLS history + public records before going under contract. Tabs: 🚦 Go/No-Go (3-state red-line gates → any Fail = NO-GO, Unknown = CAUTION, all pass = GO; editable money-at-risk meter of sunk vs refundable), 👥 Who Does What (three lanes, below), 🔗 Verify Online (address-aware link launcher: CAD by county, FEMA, USDA soil, permits, Zillow/Redfin, Street View, GreatSchools). localStorage persistence. Two rules to teach every buyer: “Free before fee” (clear the free gates before spending) and “Credit ≠ cure” (for roof/structural/insurability, require replace/repair by a licensed pro + warranty + reinspection, never a cash credit that leaves the buyer owning the defect). This workflow prevents buyers from paying option and inspection costs before reviewing material conditions already disclosed or available in public records.…Action-Split.html in the property folder, and the cockpit’s “Who Does What” tab): 🤖 AI agent (me) = everything obtainable from public data (CAD value/legal/sqft/history/owner-of-record, public listing price/DOM/relist history, school ratings, entry facing/Vastu, builder ID + review rating, neighborhood CAD comps, source-conflict detection, public permit-portal search, FEMA flood-zone query); 🧑💼 Human agent / licensed pros = ONLY licensed/physical/transactional work I cannot do (full MLS private remarks + CMA + closed comps, order the HOA resale certificate, order a new survey, lender flood determination, official tax certificate + title commitment, bind insurance, schedule/attend inspection + property access, draft/submit offer & deliver TREC notices); 🗝️ Seller = condition truth (disclosure §5.008, replace-vs-credit posture FIRST, roof invoice/warranty/transfer, foundation records, prior inspections, CLUE claims, permits, survey+T-47, builder name). Never place an AI-doable item in the human lane. Actually fetch the AI-lane items; do not merely promise them.journey.json.value and the manifest were updated. If skipped, the report and
manifest state the permitted reason.journey.json.contract (or marked unverified); flattened e-sign pages were rendered and read visually (checkbox + every signature/date block confirmed, not inferred from a “Verified” watermark); the selected basis, party names, timing/5 p.m. cutoff, preconditions (e.g. option fee paid), and delivery were all checked; earnest-money outcome stated; no termination right asserted unless it traces to the contract and is agent/attorney-confirmable.Inspection-Repair-Budget.html exists — it is MANDATORY whenever an inspection report is present (never skipped, never gated on the user asking); it was generated from the template with its two data files; every finding maps to one handling method, one specialist type, and one bundle; each DIY-eligible row has a stop condition, material kit, and retailer links; page images render; and there is no mobile overflow.