Gotchas
These are the failure modes that turn a plausible-sounding answer into a wrong one. Each is a standing entry in the Skill and was hit by a real analysis in this repo. Read this before trusting any number the toolkit returns.
1. A field's presence is not its population
A field can be in the schema and empty for most of the data. Socrata datasets often carry a field populated for only a recent operational window, or absent for most of the history. Computing a rate or total over all rows in that case silently understates or fabricates the answer.
The defence: before trusting a field, check count(field) / count(*) and, for a date-bounded field, the min/max of the window in which it is populated. The field_coverage and coverage_audit tools do exactly this.
Cambridge Parking Tickets(vnxa-cuyr, 3.07M rows since 2014) hasamount_duepopulated for only 146,261 rows (2025-07 through 2025-12, 4.8%) — and it means current outstanding balance, not the original fine. It reads$0for a paid ticket and a voided/dismissed one, and includes escalated late fees for unpaid ones. It cannot be summed into a "revenue lost" figure.
2. Query with the machine field name, not the display name
The catalog carries two names per field: columns_name (human display, e.g. "Planning Board Special Permit") and columns_field_name (the SODA API field, e.g. pb_special_permit). The SODA API only accepts the field name. Querying with a display-derived guess does not error — it silently returns zero rows, which reads as "no match" rather than "wrong field name."
The defence: always confirm against columns_field_name before writing a $where/$select or a join. search_datasets and describe_dataset surface the field name on purpose.
3. A zero-result search can mean "wrong dataset era," not "nothing on record"
Before concluding an absence, check the dataset's own date coverage (min/max of its date field), not just field population within it.
Building Permits: Addition/Alteration(qu2z-8suj) andBuilding Permits: New Construction(9qm7-wbdc) cover only work issued from 2018-07 onward. Older one- and two-family permits live inBuilding Permits: 1 and 2 Family - DEPRECATED(52p4-36ct, 2013 through 2018-07), which has a different schema (plannumber/applicationdate/classvsid/issue_date/permit_type) and a compoundLocation-typeaddressfield that 400-errors onLIKE/upper(). Match onparcelnumberinstead.
describe_dataset with probe=true catches this without a full pull.
4. Civic agreements are not portal data
Special permits, leases, dispositions, commitment letters, and Council orders exist as PDFs and hearing records, never as structured datasets. "What did developer X promise the City" cannot be answered from Socrata at all — the portal contributes parcel/ownership corroboration only.
Expect three separate legal instruments, and do not conflate them:
- the Planning Board special permit (zoning; its conditions may require a lease without being one);
- a municipal property disposition under M.G.L. Ch. 30B + Municipal Code Ch. 2.110 — where the money terms live; a two-thirds Council vote, a City Manager Disposition Report, and an RFP; and
- floor amendments extracted at the Council vote — binding in practice, but often existing only in the hearing record and press coverage, in no filed document.
5. Existence is not delivery
"The decision contains this condition" is settled by reading the document. "It was built / opened / leased / paid" is not — a document or announcement establishes intent as of its own date, and nobody circles back to report the thing that quietly didn't happen.
The defence, two moves:
- Search bounded to after the claimed date — "still vacant," "one year on," the following year's coverage.
- Check the counterparty's own website — an organisation that genuinely operates a facility says so on its own site.
Never let a WebFetch summary carry a status claim. WebFetch answers via a small fast model that reliably flattens future tense into past — "future first-floor day care space" and "will open this fall" both collapse into "opened" under paraphrase. Fetch the page and read the verb in the original sentence. This rule exists because a first draft of the 40 Thorndike report asserted a day care had opened when it never did.
6. Special-permit numbers show up in more places than the obvious log, inconsistently formatted
A Planning Board number appears, inconsistently formatted, on far more datasets than the "Development Log" family. Building Permits: Addition/Alteration (qu2z-8suj, field planning_board_number) and Building Permits: New Construction (9qm7-wbdc, field special_permit_number) both carry one — as free text mixed in with building-permit IDs and BZA case numbers in the same field (PB123, 123, 231A amendment #5, BZA-016894-2018). For any "every permit that touches X" question, check building permits too, and strip BZA-prefixed and long/dash-suffixed tokens before treating a value as a PB number.
Older permit numbers are also inconsistently zero-padded across sources — PB20 vs PB020, sp1 vs sp01. Strip leading zeros (and prefix letters) before comparing or joining, or matches silently drop.
7. A spatial nearest-match is corroboration, not proof
DuckDB's spatial extension loads cleanly (INSTALL spatial; LOAD spatial;) and is useful for polygon/point work. But a nearest-point join between two datasets that weren't designed to key off each other (e.g. attributing an open-space polygon to the nearest Development Log project by lat/lon) is corroborating evidence only, not proof of attribution. State the match distances and flag the ambiguity rather than asserting a link the data doesn't actually encode.
In the POPS analysis, a PUD-zone → nearest-project sweep surfaced 8 candidates; only 3 verified as real commitments on reading the actual decision. A majority of spatial leads turned out negative — the value was in ruling them out with the same rigor as the positives.
8. Addresses can be filed under the wrong number
Cambridge Property Database (eey2-rv59) files 40 Thorndike St under 48 Thorndike St, so a plain address search returns zero rows. Resolve the address to a map_lot via Cambridge Address Points (4ftb-8ne5, field full_addr) and query on map_lot — the same move that 52p4-36ct's compound address field forces.
9. Scanned PDFs have no text layer
City-hosted decision PDFs (Planning Board decisions, special permit filings) are often scanned images with no text layer — pdftotext returns near-empty output and web-fetch can't extract them. It isn't universal: newer CDD staff memos (2025 vintage) have a real text layer and pdftotext -layout works. Always try pdftotext first, wc -c the output, and only fall back to page-by-page image rendering if it comes back near-empty. For page counts on a scanned file, pdfinfo is reliable where a naive /Type /Page byte regex is not.
10. "Developer commitments" is not a portal category
There is no portal dataset of Planning Board special permit decisions. search_catalog on "special permit" / "planning board" surfaces only the already-checked Board of Zoning Appeal Requests and an unrelated report. Board of Zoning Appeal Requests (urfm-usws) is a false friend — its open-space fields measure a zoning density metric (usable open space as a percentage of lot area), not public accessibility. The closest thing to a systematic index is the zoning-district GIS layers, and even those locate where a zone applies, not what any given permit requires.
11. A dataset can be stale relative to its own source
The Open Space layer (88i7-ga9f) was last synced to Socrata 2024-11-27; its underlying GIS source was edited 2026-06-16 — a seven-month-plus lag long enough that recently completed spaces are missing from the public inventory. Surface freshness, and prefer min/max of a date field over a dataset-level "last edited" timestamp when judging whether a query is current.
12. Don't assume a clean join will stay clean
ixg8-tyau (Budget – Salaries, field department) and 5bn4-5wey (Budget – Operating Expenditures, field department_name) joined on plain equality for FY2027 — 15 of ~17 departments, no normalisation. Don't assume that holds for other dataset pairs; department/agency naming isn't consistently keyed portal-wide. Treat a clean join as a per-pair result, not a guarantee.