Skip to content

Releases

FactLot 0.5.0

Choose a tag to compare

@github-actions github-actions released this 07 Nov 11:01

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 and registry_tx_equity_candidates with 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_coverage and lookups in the county say why. registry_coverage on 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_candidates for 1–50 subjects in one call, each with your id and its urn,
    or county_fips and apn, and the rule parameters once. Each entry of results is 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 runs calc_tx_unequal_appraisal or 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 under rules.tx.property_tax_protest_deadline@2 exactly as
    the single tool answers it, echoed with its id; an item with invalid input carries its own
    error and the others are still answered. summary counts items per deadline with the earliest
    and latest; the rule, citations and disclaimer appear once. A rules tool, never a registry lookup.
    Formula rules.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, assembleTxEquity and the year, roll, shaping and tier helpers);
    registryTxEquityCandidates uses them and its output is unchanged. hostedWithheldCounties()
    (TypeScript) and HOSTED_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_candidates keeps its Tarrant error.
  • Registry pack builder: --exclude-county leaves 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_pacs34 reads 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 $limit or limit is recorded as row_cap on the manifest, and
    limit_saturated tells 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 and RegistryUnavailable answers are not counted. Over the cap
    a registry call answers 429 RegistryQuotaExceeded (an MCP tool error with isError), naming
    the cap, the reset time and the plan that lifts it; other tools keep working. Registry responses
    carry X-FactLot-Registry-Quota-Limit, -Remaining and -Reset.
  • Hosted API: /v1/plans lists each plan's registry lookups and key count, a quotas object with
    the counting rules, and checkout_open; the API root carries checkout_open too.
  • Hosted API: agents can buy a key themselves. POST /v1/billing/checkout also returns claim_url
    and expires_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 with by (agent or page),
    and 404 when unknown or expired. Polling is limited to once every 2 seconds per claim. The API
    root and /v1/plans describe the flow in a checkout object.
  • 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 with Accept: text/markdown.
  • Hosted API: Team subscriptions hold up to 5 keys: POST /v1/billing/keys adds one (shown once)
    and DELETE /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/account reports registry_lookups (used, limit, resets_at), the subscription's
    keys (count, limit, ids, dates and last_used_at), renewal_at (or ends_at for 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_status and account_usage MCP tools, listed only to callers with a key and
    never counted as lookups, and GET /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_SECRET secret.

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
    connector https://api.factlot.com/mcp/signin and 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...
Read more

Registry sample: District of Columbia (2026-11-07)

Choose a tag to compare

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 -t passes). SHA256SUMS and ATTRIBUTION.md for 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 by scrub_mailing_address; 11,571 owner facts,
    0 not read as organisations by is_organization; PRAGMA integrity_check ok.
  • 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 main after v0.5.0 is merged). Consider marking
    registry-sample-dc-2026-11-04 as superseded in its notes.

FactLot 0.4.0

Choose a tag to compare

@github-actions github-actions released this 06 Nov 17:54

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/tools and the factlot Python package and are rebuilt by scripts/gen-gsa-lease-comps.py from 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 replaces fact_validate in 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 a human_verified fact, with the quote-checked parts under checked and the rest under confirmed_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 and as_of warnings.
  • calc_reconcile_dcf and calc_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_files reads 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 under scripts/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 through runDraftRule / 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_reason are 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 for calc_tx_unequal_appraisal; owner names, mailing addresses and ownership facts are never returned. Texas parcels only.
  • registry_get: a parcel's record carries value_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_code and parcel.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 to calc_tx_unequal_appraisal at 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_option and lease.termination_option.
  • doc_verify_facts: verified now 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 to needs_review (check consistency). A true/false value, an enum the quote does not word, or a paraphrased summary goes to needs_review with the new check coverage and a reason naming each part. free_rent.applies_to: base_rent and a natural breakpoint 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 an InputError asks for role. Two or more amendments need effective_date; same-day amendments conflict instead of superseding.
  • doc_abstract_lease: an amendment effective after as_of is pending: its values are under pending and pending_facts with 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_checked lists the parts of an extracted lease fact that its quote does not confirm. fact_package_verify reports checks_run, checks_not_run and values_not_checked; valid is unchanged. doc_verify_claim accepts 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 under late_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 in lease_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_irr hurdles, which accepted values up to 10) above 1 in absolute value, or min_dscr above 10, in any calculator or in lease_comp_validate is 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 urllib user 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.md and ingest.md list as_of as required and describe the stricter checks; the registry-unavailable message links the current DC sample.

FactLot 0.3.0

Choose a tag to compare

@github-actions github-actions released this 04 Nov 08:27

Added

  • Registry: CMBS loan-level facts from SEC Form ABS-EE (Regulation AB II, EX-102): the many-valued debt.cmbs_loan gives, 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 a located_on link 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.parent and controls links 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 are fl:entity:us-sec:<CIK>. scripts/registry/sec_edgar.py reads the declared contact from FACTLOT_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 .xls workbooks).
  • 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; structured lease.exclusive_use (carve-outs), lease.assignment_sublease (consent standard), lease.rofr/lease.rofo details and co-tenancy occupancy and named tenants, with numbers and consent wording checked against the quote.
  • fact_package_create and fact_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_pdf and the local MCP tool doc_read_pdf (pypdf via factlot[pdf], the OCR hook for scanned pages); ocr_pages marks OCR pages in citations. Python extras pdf and signing. @factlot/tools adds runToolAsync, signFactPackage and canonicalJson.
  • Lease comps: a lease.comp fact shape (rent, escalations, free rent, TI, commissions, term, area, expense basis, who confirmed it, verification) and lease_comp_validate, which validates a comp and derives its net effective rent (calc.net_effective_rent@1), in both SDKs; docs/lease-comps.md describes how firms can trade comps without a central database (rights model, no personal data).
  • Registry: gsa_leases loads 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_exhibits builds comps from SEC Exhibit 10 leases whose values were verified with doc_verify_facts.
  • registry_get takes as_of (facts as they stood on a date) and warns when a parcel's area facts are implausible; MCP fetch of a registry fact's fl: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) and calc_cam_reconciliation_batch (calc.cam_reconciliation_batch@1: every tenant in one call, each statement identical to calc_cam_reconciliation, with a building reconciliation that explains the unrecovered amount).
  • calc_rent_schedule, calc_net_effective_rent and calc_compare_proposals take an optional unit (kw, mw, unit, acre, key, room, space; default sf): labels and warnings follow the unit, numbers are unchanged, and unit and rate_unit appear only when it is given. Claude Code plugin skill multifamily-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 marked status: "draft" with a DRAFT warning. scripts/rules/check_draft_tables.py diffs 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) and calc_exchange_1031 (calc.exchange_1031@1: deadlines, identification tests, cash and mortgage boot; not tax advice).
  • Claude Code plugin skills cre-valuation and exchange-1031; cre-loan-sizing covers 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) and docs/registry/rights.md (licence, AI use, redistribution and attribution for all 126 reviewed sources), from scripts/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 in spec/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; search returns no results and a note when nothing matches. Calculator steps show rounded numbers in formula text (results unchanged).
  • /openapi-3.0.json is the curated Custom GPT Actions subset (at most 30 operations); /openapi.json (3.1) lists every tool, and /openapi-3.0.json?operations=all gives the complete 3.0 document.

FactLot 0.2.0

Choose a tag to compare

@github-actions github-actions released this 04 Nov 04:39

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.sale facts 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_sf and others (see docs/fact-model.md).
  • Document verification: doc_verify_facts, doc_abstract_lease, doc_compare and doc_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 coverage object comes with empty results and with registry_get NotFound, and a malformed URN is a format error. New tool registry_coverage lists 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_space accepts 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_get and registry_owned_by show 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) and npm 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; --reread runs each adapter on its archived file and compares counts. python -m factlot.registry.maintain re-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 in docs/registry/quality/tier1-2026-11-03.md.

Changed

  • fact_validate rejects unknown fields, valid_from after valid_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/schema objects are strict, matching the published JSON Schema (additionalProperties: false).
  • OpenAPI documents omit registry_* operations unless the server has a registry loaded, as /v1/tools and 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> --all re-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_amortization and the new total_lease_cost are 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, via usable_sf or load_factor) and per-proposal operating_expenses (gross, nnn, base_year, expense_stop with growth), so mixed gross and NNN proposals compare like for like; landlord downtime_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-line admin_fee_applies) and occupancy (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: optional stress table (NOI, rate and value shocks; max loan, binding constraint, LTV, DSCR and debt yield per scenario) and existing_loan_balance refinance shortfall. Results without them are unchanged.
  • calc_rent_schedule and calc_net_effective_rent warn 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 a min_dscr above 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 the YYYY-MM-DD format.
  • calc.renew_vs_relocate warns when relocate.overlap_months is set but current_rent_monthly is 0 or missing (double rent priced at $0); calc.compare_proposals warns when proposals commence on different dates; calc.walt states its day count (actual days ÷ 365.25) in the steps.
  • /v1/tools lists registry tools only when a registry is loaded, like MCP tools/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 mirror normalizeName): apostrophes are dropped, what a roll appends to an owner (ET AL, a DBA trade name, a dangling & or THE) is removed, NO 1 reads as 1, consecutive initials run together, and common abbreviations spell one way (PPTIES/PROPERTIES, CTR/CENTER, STREET/ST). Registry files built before this change need python -m factlot.registry.maintain registry.db --all (re-keys the name index in place). is_organization reads a spaced L P as 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 without name_key_version 2 in its meta table (such as registry-sample-dc-2026-11-03) is searched with the earlier keys until maintain --all re-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's weight_by description named area_sf and annual_rent; it now names the accepted values area and rent, and the tool description states the day count.
  • calc.debt_sizing: constraints that allow less than one rounding increment (for example value: 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 advertise https://api.factlot.com.
  • The registry parcel total on factlot.com and in llms.txt is 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...
Read more

Registry sample: District of Columbia (2026-11-04)

Choose a tag to compare

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)

Choose a tag to compare

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

Choose a tag to compare

@github-actions github-actions released this 03 Nov 17:53

Added

  • Hosted FactLot at https://api.factlot.com (deploy/cloudflare, a Cloudflare Worker running the unchanged @factlot/server handler): 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 the registry_* 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 tightened is_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

Choose a tag to compare

@github-actions github-actions released this 03 Nov 08:06

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, and site/llms.txt; llms.txt now 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 a retention header (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/calc and factlot: 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/calc also exports npvAt.
  • @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; runTool returns 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, with search and fetch
    over a built-in knowledge base and schema_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.
  • factlot Python 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/vectors and 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), six registry_* 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, obeys robots.txt
    including Crawl-delay, rate-limits per host, reads JSON download listings, names archived
    files from Content-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
    extracted fact 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 under registry/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, honours Crawl-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, and decide_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 with Retry-After), per-key per-tool usage metering with a jsonl record and an
    admin GET /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_noise added 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...
Read more

Registry sample: District of Columbia (2026-11-03)

Choose a tag to compare

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.