Repository navigation
Releases
FactLot 0.5.0
This release brings Texas to the hosted registry, paid Pro and Team plans with metered registry
lookups, sign-in from Claude and ChatGPT without a key header, two model-backed tools for Pro and
Team, and batch tools for Texas protest work. The hosted registry serves Maryland, the District of
Columbia, Juab and Summit counties in Utah, and Harris, Dallas, Travis, Collin, Denton and
Williamson counties in Texas: 349,869 commercial parcels in 33 counties. Tarrant County is not
served while the publisher's terms for AI use are unclear. Hosted reports are deployed but not
yet open: until they open, report_create is not listed and POST /v1/reports answers 503
ReportsUnavailable.
No calculator's numbers change. Two changes need action from some users: the Texas deadline rule
renames recommended_file_by to conservative_file_by, and a registry file built with an earlier
release should be run through python -m factlot.registry.maintain <db> --purge-persons (see
Changed).
Added
Hosted registry and Texas
- Hosted registry lookups on api.factlot.com, on every plan: Maryland, DC and two Utah counties
(128,854 parcels), and six Texas counties (Harris, Dallas, Travis, Collin, Denton and Williamson;
221,015 parcels) with value history by tax year andregistry_tx_equity_candidateswith its batch
form. Texas entity lookups cover the 68,804 Comptroller entities that own a hosted parcel. Tarrant
County (48439) is not served: none of its parcels, links or IDs are hosted, and
registry_coverageand lookups in the county say why.registry_coverageon the hosted API adds
hosted(withheld counties, and entity registries served in part). Hosted equity candidates are
computed with the SDK's rule, so the hosted answer equals the SDK's for every parameter value. registry_tx_equity_candidates_batch(POST /v1/registry/tx_equity_candidates/batch):
registry_tx_equity_candidatesfor 1–50 subjects in one call, each with youridand itsurn,
orcounty_fipsandapn, and the rule parameters once. Each entry ofresultsis that
subject's answer or its own error (Texas-only and Tarrant checks per subject; a miss carries
coverage); the selection rule and the notice are given once. Candidates per subject only: it
never runscalc_tx_unequal_appraisalor picks comparables. On the hosted API each answered
subject counts as one registry lookup.rules_tx_property_tax_protest_deadline_batch(POST /v1/rules/tx_property_tax_protest_deadline/batch):
Texas protest deadlines for 1–200 notices in one call. Each item (id,tax_year, optional
notice_delivered_date) is answered underrules.tx.property_tax_protest_deadline@2exactly as
the single tool answers it, echoed with itsid; an item with invalid input carries its own
errorand the others are still answered.summarycounts items per deadline with the earliest
and latest; the rule, citations and disclaimer appear once. A rules tool, never a registry lookup.
Formularules.tx.property_tax_protest_deadline_batch@1; shared vectors in
spec/vectors/rules/tx_property_tax_protest_deadline_batch.json.@factlot/tools: the Texas candidate rule as pure functions (selectEquityCandidates,
equityReturned,assembleTxEquityand the year, roll, shaping and tier helpers);
registryTxEquityCandidatesuses them and its output is unchanged.hostedWithheldCounties()
(TypeScript) andHOSTED_WITHHELD_COUNTIES/hosted_withheld_counties()in
factlot.registry.tx_equity(Python) list the counties a hosted FactLot service does not serve,
by 5-digit county FIPS with the reason. Self-hosted use is unchanged;
registry_tx_equity_candidateskeeps its Tarrant error.- Registry pack builder:
--exclude-countyleaves a county out of a hosted pack entirely; a file
with more than 50,000 entities keeps only those that own a hosted parcel; entity names are indexed
by 1–3-character grams, so a name search no longer scans every name. - Registry: Texas appraisal-roll readers.
tx_pacs34reads the PACS 8.0.34 export layout, maps a
district's own state-code table to Comptroller categories, leaves out codes the table does not
list, and stores organisation owners only. County adapters: Andrews (tx_andrews, the 2026
real-property workbook with the district's 2025 state-code table), Bastrop (tx_bastrop, by
parcel-use code), Ector (tx_ector, only the commercial and multifamily classifications the file
states), Midland (tx_midland_web; it refuses a file with a birth-date column and does not use
mineral or personal-property codes for scope) and Orange (tx_orange, state codes; it reads no
owner fields). - Collector: a download URL's
$limitorlimitis recorded asrow_capon the manifest, and
limit_saturatedtells a row count that reached the cap from a complete extract. - Registry source records (
docs/registry/quality/audit-2026-11-06/): quality reviews of Maryland
SDAT, 57 New York ORPTS counties, MassGIS (no county passes) and six single-county loads; an
Indiana statewide parcels adapter whose source stays unverified (its licence text grants no right
to copy or redistribute); terms reviews, with nothing loaded, for 13 Indiana counties and two
Florida sources (Hillsborough, Miami-Dade).
Plans, keys and accounts
- Hosted API: registry lookups are metered. Without a key, 25 a day per IP address (UTC day); Pro
5,000 and Team 100,000 a month per subscription (calendar month, UTC), shared by its keys. A
lookup is charged only when answered:registry_coverage, coverage misses (a county or system
that is not loaded), input errors andRegistryUnavailableanswers are not counted. Over the cap
a registry call answers 429RegistryQuotaExceeded(an MCP tool error withisError), naming
the cap, the reset time and the plan that lifts it; other tools keep working. Registry responses
carryX-FactLot-Registry-Quota-Limit,-Remainingand-Reset. - Hosted API:
/v1/planslists each plan's registry lookups and key count, aquotasobject with
the counting rules, andcheckout_open; the API root carriescheckout_opentoo. - Hosted API: agents can buy a key themselves.
POST /v1/billing/checkoutalso returnsclaim_url
andexpires_at;GET /v1/billing/claim/<token>answers 202 while the checkout is unpaid, the
key once (200, with plan, limits, account and MCP URLs), then 410 withby(agentorpage),
and 404 when unknown or expired. Polling is limited to once every 2 seconds per claim. The API
root and/v1/plansdescribe the flow in acheckoutobject. - factlot.com: the page after checkout is a setup guide: the key once with copy buttons, setup for
Claude Code, Claude Desktop and claude.ai, ChatGPT, Cursor and VS Code, Python and curl, a "Test my
key" button, and what counts as a lookup. A revisit shows the guide without the key and a link to
recover access.GET /v1/onboarding(?plan=pro|team) serves the same guide as JSON, or as
Markdown withAccept: text/markdown. - Hosted API: Team subscriptions hold up to 5 keys:
POST /v1/billing/keysadds one (shown once)
andDELETE /v1/billing/keys/{id}revokes one, never the last. - factlot.com/account: paste a key to see the plan, this month's lookups as a bar, a 30-day chart by
tool and region, each key and its last use, the renewal date, the masked address Stripe sends
receipts to, the hosted regions with their source dates, what's new and where to get help; open
the billing portal to change card or cancel; replace or add keys; see connected apps and
Disconnect them. The account pages share the site's stylesheet. - Hosted API:
/v1/accountreportsregistry_lookups(used, limit, resets_at), the subscription's
keys (count, limit, ids, dates andlast_used_at),renewal_at(orends_atfor a subscription
set to cancel),support,changelog, connected apps (DELETE /v1/account/connections/{id}
disconnects one) and when an inactive record is deleted. - Hosted API:
account_statusandaccount_usageMCP tools, listed only to callers with a key and
never counted as lookups, andGET /v1/account/usage: charged lookups per UTC day for the last 30
days (up to 90), by tool and by region prefix. - factlot.com/account: email recovery for a lost key. Enter the email you paid with; a signed link
that works once for 15 minutes shows the account and issues a new key (at the plan's key limit it
replaces the oldest key and says so). The answer is the same whether or not the address has a
subscription. A welcome email after purchase gives the setup steps and a link to /account (never
the key). Email sending from factlot.com is not yet set up: until it is, the recovery form says
so and no welcome email is sent. A self-hosted deployment needs the Email Service binding and the
RECOVERY_SIGNING_SECRETsecret.
Sign-in from Claude and ChatGPT
- Hosted API: sign in with OAuth 2.1, so Claude (Desktop, claude.ai, mobile) custom connectors and
ChatGPT developer-mode apps, which cannot send a key header, can connect a paid plan. Add the
connectorhttps://api.factlot.com/mcp/signinand click Connect; a FactLot page shows which app
is asking and where access goes, and offers Connect with my key (the key is pasted once, in a
POST) and Continue on the free tier (an emailed sign-in link follows once email sending is set
up). A connection is tied to the subscription, not the key: it has the plan's limits and quotas
exactly as a key does, keeps working when the key is replaced, answers 403 while billing is
failing, and is revoked when the subscription ends. A free connection gets the free limits (60
requests a minute, 25 registry lookups a day) counted per connection instead of per address.
Access tokens last an hour; refresh tokens 30 days, rotated on every refresh. Standards: PKCE
(S256 only), exact redir...
Registry sample: District of Columbia (2026-11-07)
FactLot Registry sample: District of Columbia (2026-11-07)
A small, real FactLot Registry for trying the registry_* tools: 18,077 commercial, industrial
and multifamily parcels in the District of Columbia, 5,513 registered business entities that own
them, 7,931 ownership links, 396 buildings (condominium units and other parcels at one street
address rolled up), zoning districts for 16,648 parcels and the latest recorded sale for 12,594,
each fact cited to the public file and record it came from.
This release replaces registry-sample-dc-2026-11-04. The sources and source dates are the same;
the file is rebuilt with FactLot 0.5.0's owner rule (a person's own name followed by a generic
business word, such as "SMITH JOHN PROPERTIES", is read as a sole proprietor and its name and
mailing address are not stored), which removes one owner of record (11,571 owner facts, was
11,572), and with contact names that are not organisations ("ATTN ...", "C/O ...") removed from
owner and entity mailing addresses. Use this file instead of the 2026-11-04 one.
Sources and licences
- District of Columbia Office of Tax and Revenue, Integrated Tax System Public Extract and
Computer Assisted Mass Appraisal - Commercial, via Open Data DC (opendata.dc.gov), data as of
2026-11-02, retrieved 2026-11-03. Licence: Creative Commons Attribution 4.0 International
(CC BY 4.0). - District of Columbia Department of Licensing and Consumer Protection, Corporate
Registration, via Open Data DC, data as of 2026-11-02, retrieved 2026-11-03. Licence: CC BY 4.0. - District of Columbia Office of Zoning and Office of the Chief Technology Officer (DC GIS),
Zoning Boundaries (Zoning Regulations of 2016), Overlay Areas 2016, Record Lots, Tax Lots and
Condo Relate, via DC GIS (maps2.dcgis.dc.gov), retrieved 2026-11-04. Licence: CC BY 4.0.
Attribution: "Contains data from the District of Columbia (Open Data DC and DC GIS), licensed
under CC BY 4.0. Processed by FactLot." FactLot's changes: commercial scope only; owner names
stored only for organisations (natural-person owners, including sole proprietors under the owner
rule above, are never stored; only the SSL columns of the Condo Relate table were read); contact
names that are not organisations removed from mailing addresses; entities kept only where they own
a parcel in this sample; ownership links inferred by exact normalized name within the District
(confidence 0.9); zoning districts inferred by sampling points across each lot's polygon against
the zoning map (inferred, confidence = the district's share of the points, at most 0.95; not the
Zoning Administrator's determination); sales as published, with the acceptance code kept verbatim
and nothing marked arm's length; buildings grouped by DC condominium lot numbering or a shared
street address, each grouping labelled with its rule.
Each fact in the database carries its source document's SHA-256 (fl:document:sha256-...), the
publisher's URL, the record locator, and the source's licence, AI-use and redistribution status.
If a record names you as a person, or is wrong, email contact@factlot.com and we will remove or
correct it.
Use it
curl -fLO https://github.com/9mp96ygnbk-alt/factlot/releases/download/registry-sample-dc-2026-11-07/factlot-registry-sample-dc-2026-11-07.db.gz
gunzip factlot-registry-sample-dc-2026-11-07.db.gz
FACTLOT_REGISTRY_DB="$PWD/factlot-registry-sample-dc-2026-11-07.db" \
npx -y https://github.com/9mp96ygnbk-alt/factlot/releases/download/v0.5.0/factlot-mcp-standalone-0.5.0.tgz
The MCP server then adds the registry_* tools (registry_coverage, registry_search_address,
registry_get, registry_owned_by, registry_search_entities, registry_resolve_parcel,
registry_resolve_external and the rest). FactLot 0.2.0 or later reads this file's name and
address keys and building records.
The same DC data is served by the hosted API at https://api.factlot.com, with Maryland, two Utah
counties and six Texas counties; registry_coverage lists exactly what is hosted.
File
| File | Bytes | SHA-256 |
|---|---|---|
factlot-registry-sample-dc-2026-11-07.db.gz |
23,490,500 | 2deb519a1b20e9dfc6c7b326556d9dede013e4d9a8bb664e95e9c2a25970ae3f |
factlot-registry-sample-dc-2026-11-07.db (after gunzip) |
133,947,392 | 79687d9b86661b13e491787c063e0254fef148ce3ce5b287b7324a295b67333c |
Internal (not part of the release text):
- Local asset:
/Users/rs/Projects/FactLot/registry-data/samples/factlot-registry-sample-dc-2026-11-07.db.gz
(gzip -9 -n of/Users/rs/Projects/FactLot/registry-data/dc.db, byte-identical after gunzip;
gzip -tpasses).SHA256SUMSandATTRIBUTION.mdfor the release are in the same folder. - Checked before packing, with the repo's own rules and without printing any value: 16,890 owner
and entity mailing addresses, 0 still changed byscrub_mailing_address; 11,571 owner facts,
0 not read as organisations byis_organization;PRAGMA integrity_checkok. - Publish (launch session):
gh release create registry-sample-dc-2026-11-07 --repo 9mp96ygnbk-alt/factlot --title "Registry sample: District of Columbia (2026-11-07)" --notes-file <this file with the Internal part removed> samples/factlot-registry-sample-dc-2026-11-07.db.gz samples/SHA256SUMS samples/ATTRIBUTION.md
(the tag needs a target: add--target mainafter v0.5.0 is merged). Consider marking
registry-sample-dc-2026-11-04as superseded in its notes.
FactLot 0.4.0
Added
lease_comp_search: search the 6,994 GSA lease comps (the federal government is the tenant; not market comps for private tenants) offline and on the hosted API, by state, county, city, ZIP, radius, size, commencement, rent and type, with summary statistics and a citation on every answer. The data ship inside@factlot/toolsand thefactlotPython package and are rebuilt byscripts/gen-gsa-lease-comps.pyfrom the GSA file, checked against the release.doc_predicates: the lease predicate catalog (JSON Schema, an example, which parts are checked against the quote and how, which always need a person), generated from the checks. It replacesfact_validatein the Custom GPT Actions subset.- Document tools: a person's confirmation
{by, at, note?}turns a value whose only unverifiable parts are true/false values, enums or summaries into ahuman_verifiedfact, with the quote-checked parts undercheckedand the rest underconfirmed_by_person. It never overrides a failed quote, a contradicted number or date, or low confidence; generic labels such as "agent" or "system" are refused, and FactLot does not authenticate the name. - Document tools: rent schedules written by lease year ("Lease Years 1–5: $34.50") verify: rents and lease years are checked against the table, the commencement date against its own quote, and the step dates derived (
doc.lease_year_dates@1). calc_critical_dates(calc.critical_dates@1): renewal, termination, expansion and other notice deadlines and windows, in calendar months, with US federal holidays flagged or rolled andas_ofwarnings.calc_reconcile_dcfandcalc_reconcile_cam(@1): tie FactLot's DCF or batch CAM reconciliation out against another system's export, line by line within a tolerance, with likely causes worded as hypotheses from FactLot's conventions;python -m factlot.reconcile_filesreads a CSV or XLSX export. Tested on synthetic exports only (examples/reconciliation/tie-out); ARGUS and Yardi parity remain untested.- Docs:
connectors/frameworks/README.md, tested recipes for LangGraph/LangChain, the OpenAI Agents SDK, CrewAI, the MCP Python and TypeScript SDKs and the Vercel AI SDK (client-side tool filtering, 429 policy, User-Agent, API key), and a rerunnable script underscripts/e2e/frameworks/(not part of CI).docs/reconciliation.md. calc_tx_unequal_appraisal(calc.tx_unequal_appraisal@1): the Texas unequal-appraisal arithmetic of Tax Code §41.43(b)(3) and §42.26(a)(3). 3–200 comparables chosen by the caller, normalized by improvement or land area or compared whole, adjusted by percentages (summed or compounded), USD amounts and $/SF, each with a reason; the sorted adjusted values, the median (the mean of the two middle values for an even count) and the subject's value minus the median. Warns on mixed value bases, §42.26(d) and the §23.231 limitation, comparables in another county, fewer than 5 comparables, total adjustments over 25% and a comparable identical to the subject. Evidence calculation only: not an appraisal or an opinion of value, not legal or tax advice, and not a substitute for the advice of an attorney; it does not say whether to protest.- Draft rule pack
tx_protest_evidence_dates(rules.tx.protest_evidence_dates@1, opt-in throughrunDraftRule/run_draft_rule, not a tool): the owner's 14-day evidence date before an appraisal review board hearing (§41.43(a-1), (a-3)(3)) with a conservative date rolled back past weekends and Texas legal holidays, and the §41.67(d) requested-information cutoff (an exclusion rule, not a due date).nonBusinessReason/non_business_reasonare exported from the verified deadline rule's calendar. registry_tx_equity_candidates: candidate comparable parcels for a Texas unequal-appraisal comparison from a local registry, with the selection rule, its effective parameters, the tier each candidate came from, and each source's rights. Candidates are shaped forcalc_tx_unequal_appraisal; owner names, mailing addresses and ownership facts are never returned. Texas parcels only.registry_get: a parcel's record carriesvalue_history, its market and appraised value by tax year from every snapshot, newest first, each value cited.- Registry: Texas appraisal-roll adapters record
parcel.neighborhood_codeandparcel.building_class(Harris, Dallas, Travis; building class for Collin), and each tax year's roll is its own source, so a preliminary roll for the next year no longer retires the certified year's facts. - Claude Code plugin skill
tx-commercial-protest-evidence(twelve skills): evidence arithmetic for a Texas unequal-appraisal protest with the filing deadline, candidate comparables, the median calculation and a cited fact package. It never advises whether to protest or what value to claim, never looks up owners, and opens with the statement that it is not a substitute for the advice of an attorney. - Excel workbook
examples/reconciliation/tx_unequal_appraisal.py: the unequal-appraisal adjustment grid with live Excel formulas reconciled tocalc_tx_unequal_appraisalat zero difference, from synthetic inputs or your own (build.py tx_unequal_appraisal --input). llms-install.md: setup steps an AI coding agent can follow, hosted and local.
Changed
- The shape check enforces the documented field types of
lease.free_rent,lease.renewal_optionandlease.termination_option. doc_verify_facts:verifiednow means every part of the value was found in the quote. Prohibited uses, an exclusive's protected use and carve-outs, and ROFR/ROFO descriptions must be written in the quote; an invented item goes toneeds_review(checkconsistency). A true/false value, an enum the quote does not word, or a paraphrased summary goes toneeds_reviewwith the new checkcoverageand a reason naming each part.free_rent.applies_to: base_rentand anaturalbreakpoint are checked by their wording.doc_abstract_lease: results no longer depend on document order. A document without a role is the original only when that is unambiguous (role_inferred: true); otherwise anInputErrorasks forrole. Two or more amendments needeffective_date; same-day amendments conflict instead of superseding.doc_abstract_lease: an amendment effective afteras_ofis pending: its values are underpendingandpending_factswith a warning, and it does not control the current abstract. A rent schedule restated from a later date keeps the earlier steps (carried_forward).doc_compare:lease_not_checkedlists the parts of an extracted lease fact that its quote does not confirm.fact_package_verifyreportschecks_run,checks_not_runandvalues_not_checked;validis unchanged.doc_verify_claimaccepts a bare sentence string as the claim.calc_exchange_1031(calc.exchange_1031@2): a property identified after the 45-day deadline no longer counts. It is left out of the identification count, total value, ratio, the three-property, 200% and 95% tests and the received share, and is listed only underlate_identifications. A property received after the exchange deadline no longer counts as received for the 95% exception (before, any late receipt ended the exception).calc_dcf_pro_forma(calc.dcf_pro_forma@2): blended downtime and free rent are no longer rounded to whole months. The suite is vacant for the whole months, the rollover lease's first month collects the remaining share of rent and recoveries, and free rent abates the fraction of a month. Before, a 0.45-month blended downtime disappeared and a 55% renewal probability gave the same value as 100%. A lease that expires inside the projection always appears inlease_events, with a warning when its next lease starts after the forward year; the exit-year warning names rollover downtime.- Whole-number percents are rejected everywhere: a rate field (including the waterfall's
until_irrhurdles, which accepted values up to 10) above 1 in absolute value, ormin_dscrabove 10, in any calculator or inlease_comp_validateis an input error (HTTP 400) naming the decimal you probably meant ("pass 0.08 for 8%"), instead of computing 800% with a warning. Shares and ratios (pro_rata_share,max_ltv,min_debt_yield, occupancy,*_pct) rejected above 1 also say to pass a decimal. This replaces the warning added in 0.3.0.
Fixed
- Docs: getting-started no longer says REST and OpenAPI always list the registry tools (they are listed only with a registry) and has a hosted-API section, including that Python's default
urllibuser agent is refused at the edge; connectors README tool count and Docker tag; Claude connector steps start from the hosted URL; the site says eleven skills;document-verification.mdandingest.mdlistas_ofas required and describe the stricter checks; the registry-unavailable message links the current DC sample.
FactLot 0.3.0
Added
- Registry: CMBS loan-level facts from SEC Form ABS-EE (Regulation AB II, EX-102): the many-valued
debt.cmbs_loangives, per trust, loan and property, balances, rate, maturity, payment status, servicer and special-servicing dates, workout strategy, NOI, DSCR, occupancy and organisation tenants, on building IDs with alocated_onlink to the parcel (377 registered CMBS trusts; 21,595 facts on 16,701 buildings). No borrower names (the filings carry none) and no watchlist status (not in the SEC data). - Registry:
entity.parentandcontrolslinks from Exhibit 21 of Form 10-K filings to registered entities, matched by exact normalized name and jurisdiction (2,996 links from 109 REITs, retailers and healthcare operators); registrants arefl:entity:us-sec:<CIK>.scripts/registry/sec_edgar.pyreads the declared contact fromFACTLOT_SEC_USER_AGENT. - Registry: recorded deeds, mortgages and satisfactions for NYC commercial, industrial and multifamily lots from ACRIS, 2013 to 2026 (
transaction.deed,debt.mortgage_recorded,debt.mortgage_satisfaction_recorded, many per parcel, newest first); only organisations among the parties are stored. - Registry: NYC condominium unit lots inherit zoning and building facts from their PLUTO billing lot (labelled inferred; 99.4% of NYC registry lots now have a zoning district); NYC sales extend back to 2007 (107,090 sales; a standard-library reader handles DOF's legacy
.xlsworkbooks). - Registry: DC parcels get their zoning district's maximum floor-area ratio and height (
zoning.max_far_*,zoning.max_far_nonresidential,zoning.max_height_ft, inferred from the Office of Zoning's development standards); building energy benchmarking (energy.*) for NYC Local Law 84, DC and Denver; zoning districts for Denver, San Francisco and Minneapolis parcels. doc_spread_statement: spread a T-12, operating statement or rent-roll summary onto a published chart of accounts (multifamily, commercial, hotel after the USALI summary), with every line cited, footing checks and exceptions.doc_check_use_conflict: check a proposed use against verified exclusives and prohibited uses with a published use-category vocabulary (66 categories, 2022 NAICS); every hit is a potential conflict for human review.- Clause-level lease predicates:
lease.prohibited_uses,lease.go_dark,lease.radius_restriction,lease.relocation_right,lease.kick_out; structuredlease.exclusive_use(carve-outs),lease.assignment_sublease(consent standard),lease.rofr/lease.rofodetails and co-tenancy occupancy and named tenants, with numbers and consent wording checked against the quote. fact_package_createandfact_package_verify: canonical (RFC 8785), Merkle-hashed (RFC 6962) fact packages with optional Ed25519 signatures made with the holder's own key; FactLot never holds private keys.- Self-hosted PDF input:
factlot.read_pdfand the local MCP tooldoc_read_pdf(pypdf viafactlot[pdf], the OCR hook for scanned pages);ocr_pagesmarks OCR pages in citations. Python extraspdfandsigning.@factlot/toolsaddsrunToolAsync,signFactPackageandcanonicalJson. - Lease comps: a
lease.compfact shape (rent, escalations, free rent, TI, commissions, term, area, expense basis, who confirmed it, verification) andlease_comp_validate, which validates a comp and derives its net effective rent (calc.net_effective_rent@1), in both SDKs;docs/lease-comps.mddescribes how firms can trade comps without a central database (rights model, no personal data). - Registry:
gsa_leasesloads 6,994 federal lease comps from the GSA lease inventory (public domain), keyed to building IDs and linked to parcels, each stating that GSA leases are government-tenant leases;sec_lease_exhibitsbuilds comps from SEC Exhibit 10 leases whose values were verified withdoc_verify_facts. registry_gettakesas_of(facts as they stood on a date) and warns when a parcel's area facts are implausible; MCPfetchof a registry fact'sfl:document:sha256-…ID returns the source snapshot's metadata (publisher, URL, dates, licence and rights).- Operations calculators:
calc_multifamily_rent_roll(calc.multifamily_rent_roll@1: gross potential rent, vacancy, non-revenue units, loss to lease, concessions in weeks, months or dollars amortised over the lease, effective rent income, physical and economic occupancy, expirations by month, floor-plan summaries; a single-lease mode for effective rent after a concession),calc_capex_recovery(calc.capex_recovery@1: straight-line or level-payment recovery with interest, an optional savings cap, per-tenant split, every assumption echoed with its lease clause) andcalc_cam_reconciliation_batch(calc.cam_reconciliation_batch@1: every tenant in one call, each statement identical tocalc_cam_reconciliation, with a building reconciliation that explains the unrecovered amount). calc_rent_schedule,calc_net_effective_rentandcalc_compare_proposalstake an optionalunit(kw,mw,unit,acre,key,room,space; defaultsf): labels and warnings follow the unit, numbers are unchanged, andunitandrate_unitappear only when it is given. Claude Code plugin skillmultifamily-rent-roll.- Draft rules packs for NYC Local Law 97 (limit, emissions, $268/tCO2e penalty, 2024–2034), the Washington Clean Buildings Performance Standard (EUI target, Tier 1 test, tier and compliance date) and Boston BERDO 2.0 (emissions standard by building use and year), each citing the official text with a verification checklist. They are drafts, not tools:
runDraftRule(…, { acknowledgeDraft: true })/run_draft_rule(…, acknowledge_draft=True); every result is markedstatus: "draft"with a DRAFT warning.scripts/rules/check_draft_tables.pydiffs a draft's tables against official files for the verifying maintainer. - Valuation calculators:
calc_dcf_pro_forma(calc.dcf_pro_forma@1: property DCF from a rent roll with blended renewal assumptions, recoveries, vacancy, exit cap on forward NOI, value, NPV and unlevered IRR),calc_direct_cap(calc.direct_cap@1),calc_amortization_schedule(calc.amortization_schedule@1: partial interest-only, balloon, monthly or quarterly, 30/360 or actual/360 or actual/365, rows that foot to the cent),calc_equity_waterfall(calc.equity_waterfall@1: pref, return of capital, catch-up, IRR-hurdle promote tiers) andcalc_exchange_1031(calc.exchange_1031@1: deadlines, identification tests, cash and mortgage boot; not tax advice). - Claude Code plugin skills
cre-valuationandexchange-1031;cre-loan-sizingcovers amortization schedules. - Seven Excel reconciliation workbooks in
examples/reconciliation/(office, industrial, retail, multifamily-adjacent, hotel, lender, ASC 842) from synthetic inputs: live Excel formulas reconciled to FactLot's calculators at zero difference, regenerated by a standard-library script. ARGUS parity is stated as untested. - Generated
docs/registry/coverage.md(per state and county: what is loaded, owner names, entity links, zoning, sales, building facts, as-of dates, licence and AI use, and what is queryable today) anddocs/registry/rights.md(licence, AI use, redistribution and attribution for all 126 reviewed sources), fromscripts/registry/gen_coverage_docs.py.
Changed
- Registry owner rule: credit unions, national banks written "N.A.", federal savings banks, government bodies, condominiums and boards of managers, housing development fund corporations, MERS, Fannie Mae and Freddie Mac under their legal names, securitization trusts and foreign GMBH/PLC now read as organisations; a surname that is also an organisation word ("JOHN BANK", "CHURCH, MARY") no longer makes a person an organisation. Registries built earlier need
python -m factlot.registry.maintain <db> --purge-persons. Shared synthetic vectors inspec/registry/organizations.json. - ID spec 0.3: parcel IDs keep where an APN's separators fall ("12-34" ≠ "1-234"); NYC BBL, Los Angeles AIN, Cook PIN and DC SSL normalize to their zero-padded form ("3-1234-56" = "3012340056"); LEIs must pass their ISO 17442 check digits. Earlier IDs still parse (
normalizeApnV1/normalize_apn_v1), and registry files record the rule they were built with, so published files keep working. - Registry address search matches whole words: a house number, or a street name with its type; a city name alone no longer matches unrelated streets. Registry messages name counties ("Pinal County, AZ (04021)") from the Census Bureau county FIPS list.
- MCP: input-validation errors return the same JSON error object as REST;
searchreturns no results and a note when nothing matches. Calculator steps show rounded numbers in formula text (results unchanged). /openapi-3.0.jsonis the curated Custom GPT Actions subset (at most 30 operations);/openapi.json(3.1) lists every tool, and/openapi-3.0.json?operations=allgives the complete 3.0 document.
FactLot 0.2.0
Added
- Registry zoning, building and sale facts on parcels. NYC PLUTO adds zoning district, overlays, special districts, maximum floor-area ratios, built FAR, buildings, floors, units and area by use for 116,887 New York City parcels. DC zoning districts (
inferred, with a confidence, from each lot's polygon sampled against the zoning map) cover 16,648 of 18,077 DC parcels.transaction.salefacts come from NYC Department of Finance sales (12,445 sales, 2024 through September 2026) and the DC tax extract (latest sale per parcel), with no party names and arm's length not stated; $0 transfers and prices repeated across several lots are flagged. A parcel can hold many sales, newest first. - New predicates:
zoning.*,parcel.built_far,parcel.num_floors,parcel.num_buildings,parcel.units_*,parcel.area_by_use_sfand others (seedocs/fact-model.md). - Document verification:
doc_verify_facts,doc_abstract_lease,doc_compareanddoc_verify_claim(MCP, REST/v1/doc/*, every provider format, Python SDK). Your agent extracts each value with a verbatim quote and page; FactLot checks deterministically that the quote is on that page and states the value, and returns cited facts with character spans, review items and rejections with reasons. No model runs and nothing is stored (guide). - Registry misses now say whether the county, entity registry or crosswalk system is loaded: a
coverageobject comes with empty results and withregistry_getNotFound, and a malformed URN is a format error. New toolregistry_coveragelists what a registry file holds. - ID spec 0.2:
id_building, a deterministic building ID from county, ZIP and street line (fl:building:us-11001:20002:208-massachusetts-ave-ne), shared by every unit at the address;id_spaceaccepts it. Registry building roll-up groups condo units and other parcels at one street address under a building with unit and parcel counts, each grouping labelled with how it was inferred (DC or NYC condo lot numbering, or the shared address);registry_getandregistry_owned_byshow it. - Claude Code plugin and marketplace (
/plugin marketplace add 9mp96ygnbk-alt/factlot, then/plugin install factlot@factlot): eight CRE skills (loan sizing, lease economics, CAM reconciliation, ASC 842, lease vs own and renewals, investment returns, hotel metrics, Texas protest deadline) that call the hosted FactLot tools and cite formula IDs. examples/(curl, Python REST, MCP client, offline SDK, Claude API and OpenAI Responses remote MCP) andnpm run test:hosted, a live end-to-end check of the hosted service: every conformance vector over REST, every MCP tool against REST, every site page and link, and the checkout paths.- Registry Tier 2 expansion: statewide Vermont, Arkansas and Connecticut (CC0 layer) parcels, 24 Minnesota opt-in counties, Montana apartments and industrial parcels, five more North Carolina counties (Guilford, Forsyth, Cumberland, Johnston, New Hanover) and Garfield County, Utah: 228,453 more commercial parcels in 182 counties, with review records and adapters (expansion notes).
- Registry quality audit (
python -m factlot.registry.audit): per source and snapshot, parcel and entity counts, field completeness, conflicting and implausible facts, person-like owner names (policy: zero), owner link rate by confidence and rule with the reasons the rest are unlinked, review queue, snapshot age and citation gaps;--rereadruns each adapter on its archived file and compares counts.python -m factlot.registry.maintainre-applies the person rule, re-keys the name index and review queue, relinks and queues fuzzy candidates on a built file. The Tier 1 audit is indocs/registry/quality/tier1-2026-11-03.md.
Changed
fact_validaterejects unknown fields,valid_fromaftervalid_to, inverted spans, inferred facts without sources, and computed facts with neither a formula nor a source; for Ingest predicates it checks the value's shape and that each source quote states the value.@factlot/schemaobjects are strict, matching the published JSON Schema (additionalProperties: false).- OpenAPI documents omit
registry_*operations unless the server has a registry loaded, as/v1/toolsand MCP already do (keeps Custom GPT Actions under 30 operations). - Ingest verifier: written-form patterns are ASCII-only, as in JavaScript, and a whole-number float year such as 2026.0 is accepted.
- Address search reads "Northwest", "North West" and "N.W." as "NW" (all eight directionals) and ignores a trailing suite, unit, "#" or floor. Older registry files are searched by old and new keys until
python -m factlot.registry.maintain <db> --allre-keys them. - ID spec 0.2, suite rule 2: "Suite 1-20" and "Suite 120" no longer share an ID; "Third Floor" equals "3rd Floor"; "Apt 4B" equals "4B". Earlier IDs still parse, and the old rule stays callable.
calc.lease_remeasurement_asc842@2: the amortization schedule foots to the cent. Every row rolls forward exactly, the final period trues up the liability and ROU asset to zero (shown as a step),total_interest,total_amortizationand the newtotal_lease_costare row sums, and the rounding rule is stated in the steps. Some schedule cells move by cents; headline figures are unchanged.calc.compare_proposals@2:area_basis(rentable or usable, viausable_sforload_factor) and per-proposaloperating_expenses(gross, nnn, base_year, expense_stop with growth), so mixed gross and NNN proposals compare like for like; landlorddowntime_months; the discounting convention is stated in the steps; warnings when the usable-SF ranking differs and when terms differ by 12 months or more.calc.cam_reconciliation@2:admin_fee(rate, before or after the cap, capped or not, per-lineadmin_fee_applies) andoccupancy(move-in and move-out prorate share, cap and base-year or stop deduction by days), with order-of-operations warnings for the fee.calc.debt_sizing@3: optionalstresstable (NOI, rate and value shocks; max loan, binding constraint, LTV, DSCR and debt yield per scenario) andexisting_loan_balancerefinance shortfall. Results without them are unchanged.calc_rent_scheduleandcalc_net_effective_rentwarn when commencement is mid-month (months run from the commencement day, no calendar proration).- Tool input: a field the tool does not define is now an input error naming the field and the nearest known name (
expenses.0.controlable: unknown field (did you mean "controllable"?)) instead of being ignored, which silently changed answers. Rate fields above 1 and amin_dscrabove 10 add a warning that the value looks like a whole-number percent, and a rate rejected as too big says rates are decimals. Date errors name theYYYY-MM-DDformat. calc.renew_vs_relocatewarns whenrelocate.overlap_monthsis set butcurrent_rent_monthlyis 0 or missing (double rent priced at $0);calc.compare_proposalswarns when proposals commence on different dates;calc.waltstates its day count (actual days ÷ 365.25) in the steps./v1/toolslists registry tools only when a registry is loaded, like MCPtools/list, and the registry-unavailable message says how to get a registry instead of naming a server setting. Paid plan text says no hosted registry region is open yet.- Registry owner-name normalization (
normalize_name, and its TypeScript mirrornormalizeName): apostrophes are dropped, what a roll appends to an owner (ET AL, aDBAtrade name, a dangling&orTHE) is removed,NO 1reads as1, consecutive initials run together, and common abbreviations spell one way (PPTIES/PROPERTIES,CTR/CENTER,STREET/ST). Registry files built before this change needpython -m factlot.registry.maintain registry.db --all(re-keys the name index in place).is_organizationreads a spacedL Pas a partnership only with a number or a property or business word in the name, and a trust named for a person (separate property, revocable, life estate) as a person. - Entity name search (
registry_search_entities,Registry.search_entities) uses the name keys the file was built with: a file withoutname_key_version2 in itsmetatable (such asregistry-sample-dc-2026-11-03) is searched with the earlier keys untilmaintain --allre-keys it. New files record version 2. Registry.ingest_parcels: re-ingesting a snapshot now replaces the facts an earlier run wrote for it that the adapter no longer yields, and the links derived from a replaced owner; an unchanged file still writes nothing.
Fixed
calc_walt'sweight_bydescription namedarea_sfandannual_rent; it now names the accepted valuesareaandrent, and the tool description states the day count.calc.debt_sizing: constraints that allow less than one rounding increment (for examplevalue: 1) are an input error instead of a crash on an infinite debt yield.- Hosted billing: a checkout paid by bank transfer (complete, payment still processing) now says the payment is processing and the page will show the key once it clears, instead of asking the customer to reload in a minute.
- Hosted billing: a key whose subscription Stripe does not know (for example a sandbox purchase re-checked with a live key) now stops working at its next check instead of staying active.
- The hosted OpenAPI documents advertised
http://server URLs; they now advertisehttps://api.factlot.com. - The registry parcel total on factlot.com and in
llms.txtis 1,953,805, not 1,945,117: it had counted 37,292 Tarrant parcels where the full set is 45,976, and Travis gained 4 with supplement 3. - Colorado entity adapter: the Department's appended status history (
, Delinquent January 1, 2025) is no longer part of the legal name (1.87 million of 3.13 million names), so owners written as the bare name link. - New York City adapter: an owner...
Registry sample: District of Columbia (2026-11-04)
FactLot Registry sample: District of Columbia (2026-11-04)
A small, real FactLot Registry for trying the registry_* tools: 18,077 commercial, industrial
and multifamily parcels in the District of Columbia, 5,513 registered business entities that own
them, 7,931 ownership links, 396 buildings (condominium units and other parcels at one street
address rolled up), zoning districts for 16,648 parcels and the latest recorded sale for 12,594,
each fact cited to the public file and record it came from.
Sources and licences
- District of Columbia Office of Tax and Revenue, Integrated Tax System Public Extract and
Computer Assisted Mass Appraisal - Commercial, via Open Data DC (opendata.dc.gov), retrieved
2026-11-02/03. Licence: Creative Commons Attribution 4.0 International (CC BY 4.0). - District of Columbia Department of Licensing and Consumer Protection, Corporate
Registration, via Open Data DC, retrieved 2026-11-03. Licence: CC BY 4.0. - District of Columbia Office of Zoning and Office of the Chief Technology Officer (DC GIS),
Zoning Boundaries (Zoning Regulations of 2016), Overlay Areas 2016, Record Lots, Tax Lots and
Condo Relate, via DC GIS (maps2.dcgis.dc.gov), retrieved 2026-11-04. Licence: CC BY 4.0.
Attribution: "Contains data from the District of Columbia (Open Data DC and DC GIS), licensed
under CC BY 4.0. Processed by FactLot." FactLot's changes: commercial scope only; owner names
stored only for organisations (natural-person owners are never stored; only the SSL columns of the
Condo Relate table were read); entities kept only where they own a parcel in this sample;
ownership links inferred by exact normalized name within the District (confidence 0.9); zoning
districts inferred by sampling points across each lot's polygon against the zoning map
(inferred, confidence = the district's share of the points, at most 0.95; not the Zoning
Administrator's determination); sales as published, with the acceptance code kept verbatim and
nothing marked arm's length; buildings grouped by DC condominium lot numbering or a shared street
address, each grouping labelled with its rule.
Each fact in the database carries its source document's SHA-256 (fl:document:sha256-...), the
publisher's URL, the record locator, and the source's licence, AI-use and redistribution status.
Use it
gunzip factlot-registry-sample-dc-2026-11-04.db.gz
FACTLOT_REGISTRY_DB="$PWD/factlot-registry-sample-dc-2026-11-04.db" \
npx -y https://github.com/9mp96ygnbk-alt/factlot/releases/download/v0.2.0/factlot-mcp-standalone-0.2.0.tgz
The MCP server then adds registry_coverage, registry_search_address, registry_get,
registry_owned_by, registry_search_entities, registry_resolve_parcel and
registry_resolve_external. FactLot 0.2.0 or later reads this file's name and address keys and
building records; 0.1.x can read it but matches fewer names and addresses and shows no buildings.
This is a sample. The project's full registry (1,953,805 commercial parcels in 405 counties and
14.2 million registered entities) is not yet offered as a hosted service; contact@factlot.com.
Lease comps: GSA federal leases (2026-11-04)
FactLot lease comps: US General Services Administration leases (2026-11-04)
6,994 lease comps for 7,031 federal (GSA) leases across the United States, as cited FactLot
facts: current annual rent per rentable square foot, rentable area, commencement and expiration
dates, early-termination date where stated, renewal options left, property type (office,
warehouse or special), whether the lease is fully serviced, and a building ID computed from the
street address. Lessors are stored only where they are organisations; individuals are never
stored. Each fact cites the GSA file it came from.
Read this before using them as comps. GSA leases are government-tenant leases: the federal
government is the tenant, most are fully serviced, and their rent reflects GSA's procurement and
security requirements. They are real, public, cited lease data, not market comps for every use.
Every comp says so in its notes.
Source and licence
US General Services Administration, Lease Inventory (October 2026 file), gsa.gov, retrieved
2026-11-03. US federal government work: public domain in the United States (17 U.S.C. § 105).
GSA asks to be cited: "Source: U.S. General Services Administration, Lease Inventory." FactLot's
changes: rent per RSF computed from annual rent and rentable area (comps outside $2 to $250 per
RSF a year omitted), lessor names kept only for organisations, building IDs computed, and each row
written as cited facts.
Use it
gunzip factlot-lease-comps-gsa-2026-11-04.db.gz
FACTLOT_REGISTRY_DB="$PWD/factlot-lease-comps-gsa-2026-11-04.db" \
npx -y https://github.com/9mp96ygnbk-alt/factlot/releases/download/v0.3.0/factlot-mcp-standalone-0.3.0.tgz
Then registry_search_address finds leases by street address and registry_get returns a
lease's comp, area and dates. The file is SQLite, so it can also be queried directly. To share
your own comps with other firms in the same format, see
https://github.com/9mp96ygnbk-alt/factlot/blob/main/docs/lease-comps.md and lease_comp_validate.
FactLot 0.1.1
Added
- Hosted FactLot at
https://api.factlot.com(deploy/cloudflare, a Cloudflare Worker running the unchanged@factlot/serverhandler): remote MCP at/mcp, REST and OpenAPI, free with no key at 60 requests a minute per address, and the factlot.com landing page with a pricing section. The MCP Registry entry now lists the remote endpoint. - Paid hosted plans through Stripe Checkout: Pro ($49 a month, 600 requests a minute) and Team ($599 a month, 3,000 a minute). Keys are
fl_live_bearer tokens stored only as hashes and shown once;/v1/billing/checkout(agents can start checkout and hand the link to their human),/v1/billing/portal,/v1/billing/rotate,/v1/plans,/v1/account, and an optional signed webhook. registry-sample-dc-2026-11-03: a downloadable SQLite registry of 18,077 District of Columbia commercial parcels, 5,513 owning entities and 7,908 cited ownership links (CC BY 4.0) for trying theregistry_*tools.Registry.purge_person_owners(): removes owner names that the current person-detection rule reads as natural persons from a registry built under an earlier rule (with their mailing addresses, inferred links and review rows). Run once after upgrading past 0.1.0's tightenedis_organization.- Registry Tier 2 review: 61 more counties (the rest of the US top 100 by population), 28 verified with adapters and tests across 15 states, statewide parcel sources for Wisconsin, Utah, North Carolina, New York, Maryland and Massachusetts, the Connecticut entity registry, and a review record and collector config for every source (index).
- Registry statewide loads (2026-11-03): New York outside the city, Maryland, Wisconsin, Massachusetts outside Boston and 23 more Utah counties loaded in full into the scratch registry, 477,121 more commercial parcels in 177 counties (1,008,126 in 207 counties in all, 436,190 with an organisation owner of record); counts per county in each review record.
FactLot 0.1.0
The first release. Everything below is the work since the repository was created, summarised
from its commit history; nothing was published to npm or PyPI before it.
Added
site/gained schema.org JSON-LD, Open Graph and Twitter tags, a canonical link, a "For AI agents" section, a hosted-registry early-access call to action, andsite/llms.txt;llms.txtnow opens with what FactLot does, when to use it, and how to install it today.site/: the factlot.com landing page, a static page with no build step, no scripts, and no
tracking, with deployment notes.- Customer documents policy (
docs/customer-documents.md), with controls in code: every ingest report carries aretentionheader (retain_until, default 30 days,--retain-days),--redact/IngestResult.to_report(redact=True)persists document text only as the quotes that are evidence, and the server's request log schema is fixed to metadata (REQUEST_LOG_FIELDS) with a test that no request body, tool argument, or bearer token reaches a log line. calc.lease_remeasurement_asc842@1: lessee remeasurement of a lease liability and
right-of-use asset under ASC 842 (modification, lease-term or purchase-option reassessment,
contingency resolution, residual value guarantee change; index changes warn that they are not
a trigger on their own), with the ASC 842-20-35-4 asset adjustment and gain floor and a
per-period table (operating: straight-line single lease cost; finance: interest plus
amortization). Steps cite the ASC paragraphs; judgment points (partial termination, discount
rate updates) are warnings.calc.prepayment_and_defeasance@1: prepayment premium, total payoff, and cost as a share of
balance for a fixed-rate loan under yield maintenance (PV-of-payments or rate-differential
basis, Treasury plus spread, minimum, and an explicit monthly or semiannual discounting
convention), step-down, flat percentage, or defeasance (replacement securities cost from a flat
yield or a stepped curve, plus stated costs). Both calculators ship in TypeScript and Python
with shared vectors and differential-test generators.@factlot/schema(MIT): the fact data model with provenance and AI-use rights, exported as
JSON Schema.@factlot/id: FactLot ID v0.1, deterministic identifiers for parcels, legal entities,
documents, and spaces, plus minted building, lease, and loan IDs and short IDs.@factlot/calc: deterministic calculators with step-by-step audit trails and versioned
formulas: rent schedule, net effective rent, proposal comparison, CAM reconciliation,
percentage rent, debt sizing, WALT, NPV, and IRR.@factlot/calcandfactlot:calc.lease_vs_own@1(NPV of leasing versus buying over a
horizon with financing, residual by value or exit cap, optional after-tax treatment, year-by-year
tables, and the break-even residual value),calc.renew_vs_relocate@1(NPV, discounted effective
rent, break-even starting rent, and cash-flow tables for renewing in place versus moving, including
TI net of allowance, moving, downtime, double rent, termination, and disruption costs), and
calc.hotel_metrics@1(occupancy, ADR, RevPAR, TRevPAR, and a USALI-style ladder from departmental
profit through GOP, management fees, EBITDA, FF&E reserve, and NOI), with conformance vectors and
differential-test generators;@factlot/calcalso exportsnpvAt.@factlot/tools(MIT): one tool catalog (calculators, IDs, fact validation) generating
native tool definitions for the Claude Messages API, OpenAI Chat Completions and Responses,
Gemini, Amazon Bedrock Converse, and OpenAPI 3.1 and 3.0;runToolreturns input errors as
data. Every input field carries a description, enforced by a test.@factlot/mcp: a local stdio MCP server over the whole catalog, withsearchandfetch
over a built-in knowledge base andschema_get.@factlot/server: the remote connector, one stateless HTTP service serving Streamable HTTP
MCP at/mcp, REST at/v1, and OpenAPI, with optional bearer auth, and a Docker image.- A Claude Desktop extension build (
npm run build:mcpb) that bundles the stdio server and
generates its manifest from the catalog. - Connector guides for Claude, ChatGPT and OpenAI, Gemini, Microsoft Copilot Studio, Amazon
Bedrock (including an Agents action-group Lambda with tests), and OpenAI-compatible clients. factlotPython package: a dependency-free port of the calculators and IDs that loads the
generated tool definitions and runs every tool, held to the same conformance vectors as the
TypeScript packages.- Language-neutral conformance vectors in
spec/vectorsand a TypeScript-versus-Python
differential test over random inputs. - Rules packs: versioned rules that cite their statutes and carry a verification status, with
the first pack,rules_tx_property_tax_protest_deadline, verified against the official
Texas Tax Code and Government Code text. - The registry: a Python, SQLite-backed store of cited parcel and entity facts (content-addressed
snapshots, commercial-only scope, no natural-person indexing, owner-to-entity linking,
retirement of parcels dropped from a source's latest roll), sixregistry_*tools on every
surface when a registry is configured, a data policy, and a Texas source catalog. - A polite data collector (
factlot.collector) that identifies itself, obeysrobots.txt
includingCrawl-delay, rate-limits per host, reads JSON download listings, names archived
files fromContent-Disposition, and records every decision in a manifest; a weekly GitHub
Actions workflow archives verified Texas sources as release assets. - Adapters for the Harris CAD, Dallas CAD, and Tarrant AD appraisal rolls and the Texas
Comptroller franchise-tax entity file, run on the real 2026 files (87,717, 59,645, and 37,292
commercial parcels; 3.27 million registered entities; 73% of organisation owners linked by
exact name). - A lease-ingestion prototype (
factlot.ingest) with deterministic verification of every
extracted value against a verbatim quote on the cited page, a value-in-quote consistency check
(numbers, ratios, dates, and party names must appear in the quote), a pluggable OCR step for
scanned pages with a tesseract backend, and amendment supersession. - Ingest predicates for percentage rent, co-tenancy, the renewal, termination and expansion
notice deadlines, the rent commencement date, the operating-expense base year, parking, and
the guaranty (name only; a value that reads as an address is refused), each with a value shape
and the same consistency checks as the existing ones. - An ingest review queue: a candidate whose quote is on the page but whose value is not written
in it, or whose extractor confidence is below a threshold, waits for a named reviewer instead
of being dropped or accepted (review_queue,decide; CLI--review,--accept,
--reject,--by). The queue lives in the CLI's output file; an accepted item becomes an
extractedfact whose provenance names the reviewer and the failed check, a rejected
(predicate, quote) pair is never queued again, and re-running keeps every decision. - The README states what FactLot covers today.
- Tier 1 expansion: 47 source reviews across 15 states and DC (
docs/registry/sources/), one
collector config per source underregistry/sources/<state>/, 33 adapters (New York City,
Los Angeles, San Francisco, Alameda, Riverside, Boston, Philadelphia, Cook County, Fulton,
Maricopa, King, Hennepin, Mecklenburg, Denver, Wayne, Fairfax, DC, Florida DOR, Travis, Collin,
and the New York, Colorado, Pennsylvania, DC, Florida, North Carolina, and Virginia entity
registries), each with a synthetic-fixture test. Parcels keep the publisher's own use code
(parcel.local_use_code). The weekly collector workflow covers every state, loads configs
recursively, honoursCrawl-delay, strips explicit default ports from redirect targets, and
refuses to archive an export API's "pending" placeholder. - Registry owner resolution beyond exact names: deterministic fuzzy candidates
(queue_fuzzy_owner_matches), a review queue, anddecide_owner_match; nothing is linked
without a person's accept, and accepted links carry confidence at most 0.8 and the reviewer. @factlot/server: named API keys from an environment variable or a JSON file, per-key rate
limits (429 withRetry-After), per-key per-tool usage metering with a jsonl record and an
adminGET /v1/usage, and JSON request logging (FACTLOT_API_KEYS,FACTLOT_API_KEYS_FILE,
FACTLOT_RATE_LIMIT_PER_MINUTE,FACTLOT_ADMIN_KEY,FACTLOT_USAGE_LOG,FACTLOT_LOG).- A release process: one shared version,
scripts/release/, this changelog, a tag-driven
publish workflow for npm, PyPI, and the.mcpb, issue and pull-request templates, CODEOWNERS,
Dependabot, a code of conduct, and a getting-started guide. - FactLot Bench: fourteen CRE agent tasks with independently computed ground truth, a runner,
and a scorer. - Documentation for agents (
llms.txt,AGENTS.md,docs/agents.md), the ID spec, the fact
model, the registry, rules, and ingestion.
Changed
calc.debt_sizing@2: the maximum loan is rounded down after reducing it to 15 significant
digits, so binary noise no longer costs a whole increment (a 57% LTV on $10,000,000 rounded
to $1,000 returned $5,699,000 instead of $5,700,000). Both SDKs;reduceNoise/
reduce_noiseadded to the core helpers.calc.renew_vs_relocate@2: warns when a break-even starting rent is negative (one case's
one-time costs alone exceed the other's NPV), instead of reporting an unachievable rent
silently.calc.lease_vs_own@2: warns when the loan exceeds purchase price plus closing costs (negative
equity at closing).- Tool descriptions state units, decimal-versus-percent, and periods for every field an agent
could misread (NPV rate, one-time TI allow...
Registry sample: District of Columbia (2026-11-03)
FactLot Registry sample: District of Columbia (2026-11-03)
A small, real FactLot Registry for trying the registry_* tools: 18,077 commercial, industrial
and multifamily parcels in the District of Columbia, 5,513 registered business entities that own
them, and 7,908 ownership links, each fact cited to the public file and record it came from.
Sources and licences
- District of Columbia Office of Tax and Revenue, Integrated Tax System Public Extract and
Computer Assisted Mass Appraisal - Commercial, via Open Data DC (opendata.dc.gov), retrieved
2026-11-02/03. Licence: Creative Commons Attribution 4.0 International (CC BY 4.0). - District of Columbia Department of Licensing and Consumer Protection, Corporate
Registration, via Open Data DC, retrieved 2026-11-03. Licence: CC BY 4.0.
Attribution: "Contains data from the District of Columbia (Open Data DC), licensed under
CC BY 4.0. Processed by FactLot." FactLot's changes: commercial scope only; owner names stored
only for organisations (natural-person owners are never stored); entities kept only where they
own a parcel in this sample; ownership links inferred by exact normalized name within the
District (confidence 0.9) or by the standing rule (0.85).
Each fact in the database carries its source document's SHA-256 (fl:document:sha256-...), the
publisher's URL, the record locator, and the source's licence, AI-use and redistribution status.
Use it
gunzip factlot-registry-sample-dc-2026-11-03.db.gz
FACTLOT_REGISTRY_DB="$PWD/factlot-registry-sample-dc-2026-11-03.db" \
npx -y https://github.com/9mp96ygnbk-alt/factlot/releases/download/v0.1.0/factlot-mcp-standalone-0.1.0.tgz
The MCP server then adds registry_search_address, registry_get, registry_owned_by,
registry_search_entities, registry_resolve_parcel and registry_resolve_external.
This is a sample. The full registry (1.7 million commercial parcels in 223 counties and 14.2
million registered entities, refreshed weekly) is offered as a hosted service in early access:
contact@factlot.com.