SIDE PROJECT · CURRENT
BlockReport
Philadelphia property records live across systems that do not line up. This makes one inspectable layer from them.
One row per parcel, behind one question box.
21,193 hundred-blocks, 52 ZIP partitions, 37 ranked boards.
DuckDB-Wasm recomputes every filter from a local snapshot.
34 of 34 tests pass, on snapshot 20260803T232826Z.
THE REASONING
The live explorer runs at philadelphia.blockreport.homes →
Why the attrition count is published
Philadelphia publishes assessments, deeds, permits, violations, taxes, licenses, parcel boundaries, and Census context through separate systems. Identifiers do not match, dates mean different things, and the geographic grain changes between them. The resolve step produces stable property records keyed to one canonical parcel, aggregated at hundred-block and ZIP scale, with the source date kept next to each fact.
582,030 parcel index rows became 581,566 shipped rows, and the manifest says where the difference went: 65 rows with invalid ZIPs, 42 with no coordinates. Sales get the same treatment. 178,388 of 579,199 recorded sales clear a screen for a dated consideration over $20,000 not flagged nominal, and the manifest states plainly that this is a screen, not proof of an arm's length transaction.
Without that accounting, a later question about one missing parcel has no answer except rebuilding the whole snapshot.
Client-side DuckDB-Wasm queries and partition transfer trade-offs
A map measure or a filtered count has exactly one right result for a given snapshot. DuckDB-Wasm 1.32.0, which is the DuckDB analytical database compiled to WebAssembly so it runs inside the page, executes the query in a worker off the interface thread. Change a metric, year, ZIP, category, or property filter and it recomputes from the local snapshot.
It is reproducible because the files, schema, and query definitions are versioned together, and the runtime is served from the application rather than a CDN, so the snapshot and the engine that reads it ship as one unit.
The trade-off is transfer size. The complete 20260803T232826Z artifact set is 152,206,277 bytes. Splitting by ZIP means a scoped report pays only for its scope: ZIP 19130 loads 1,787,241 bytes across four artifacts, and the citywide map reads a prepared aggregate rather than shipping every parcel to recolor a polygon. Assessment history sits in separate optional artifacts, and property reports load no analytics artifacts at all.
Plotting filings on exact dates instead of calendar year buckets
Property reports contextualize assessed value by comparing it to the surrounding block and neighborhood. The chart plots the property's assessed value as a line, the block's median and range as a band underneath, and the ZIP median as a third line. Every filed deed, sale, permit, violation, zoning appeal, inspection, certification, and license sits directly on that line at its filed date.
Records sit on their exact filed date rather than snapping to the year, so a January sale and a November permit do not stack. Nearby dates take alternating lanes above and below the line, keeping a busy permit year readable. Records with no exact day are centered in their year and labeled undated. Selecting a marker opens the filing with a plain description and a link to the public record.
The chart renders as a server-side SVG inside canonical HTML. The rail underneath lists every dated event in chronological order for screen readers.
Resolving municipal code editions across 13 city code books
Code search covers thirteen Philadelphia code books, and a search resolves the applicable edition rather than the newest one. Local law books answer as current Philadelphia law. The Fire Code stays on the adopted 2018 edition. A 2018 request returns results labeled historical with a warning to check the permit's application date.
Results are restricted to ICC, American Legal, NFPA, and phila.gov, and each is labeled a snippet rather than a complete section.
The 33 research tools and query safety boundaries
What changed at this address may need assessment history, deeds, permits, violations, zoning, comparable sales, and block context together. The tools cover four families. Property and ownership: current records, deeds, assessment and sale history, licenses, permits, violations, zoning appeals, comparable sales. Block, ZIP, and city: whole-block reports, nearby activity, market context, permit and business trends, neighborhood profiles, ranked comparisons. Philadelphia code search, described below. And one read-only query path for when no packaged tool fits, restricted to 11 allowlisted city tables and at most 400 rows, with no owner or mailing columns and no write access to the underlying record. It says so when it truncates.
Two more tools appear only while the Property Explorer is open. One writes page state through a closed schema of enumerated actions with no SQL, HTML, URLs, or coordinates in it. The other reads back what the page computed: match count, rows checked, query time. The agent reports those numbers instead of estimating them. The agent can drive the map. DuckDB still produces the number.
While research runs, the interface streams the tool choices, their completion states, and honest record counts, so a reader watches which records the answer is built from instead of waiting on a spinner and taking the result on trust.
Data privacy boundaries and excluded property fields
The deterministic path returns exact rows, counts, and sort order from a named snapshot. Static property insights take a deterministic fact-sheet check on top: unsupported numbers are removed, and fewer than two surviving claims returns no AI insight at all rather than a thin one. Web search is reserved for explicit browsing requests and for current listing, asking-price, or recent-news questions.
Ownership identity is removed from the shipped layer. The snapshot omits eight field families, including owner and co-owner names, mailing addresses, permit and violation descriptions, appeal grounds, and investigation case numbers. The query path blocks owner and mailing columns outright, and the ranked owner boards say on the board itself that they aggregate recorded names and do not infer parent companies or affiliated entities.
Missing records are never coerced to zero, and timestamps reflect source agency publication dates without artificial normalization. Public records update on different agency schedules. The system exposes record dates and conflicts; it cannot make a lagging source current. Two tax feeds are frozen where the city froze them: delinquencies are a June 2022 snapshot, balances a historical ledger through 2016.
A focused run of the agent action and explorer-return suites passes 34 of 34 tests. Leaderboard math and history carry their own suites, and the full test directory is 43 files. This page is an architecture record. It does not expose accounts, saved research, customer data, or private operational surfaces.