Skip to content

Changelog

Unreleased

Added

Changed

Fixed

Deprecated

Contracts

Security

1.0.3 - 2026-04-21

Docs-only patch. Ships everything that landed on main after v1.0.2 (PR #23's llms.txt + doc refresh) plus a cross-reference surfacing nyc_geo_toolkit.centroids_from_boundaries from nyc311.spatial (closes #24). No consumer code needs to change.

Added — machine-readable agent guide

  • docs/llms.txt — canonical llmstxt.org-style summary consumed by Cursor, Codex, Copilot, Claude Code, Aider, Zed, Windsurf, Gemini CLI, ChatGPT. Covers the public surface, typical workflows, contracts, install paths, examples, version ranges, ecosystem siblings. Added to mkdocs nav as "For LLMs / agents"; AGENTS.md gains a pointer at it.

Docs

  • nyc311.spatial module + load_boundaries_geodataframe docstring now cross-reference upstream nyc_geo_toolkit.centroids_from_boundaries (v0.4+) for polygon-centroid extraction (addresses #24 via the doc-only path — nyc311 deliberately doesn't ship a centroid helper in nyc311.spatial because the upstream helper is the one-stop shop).
  • docs/integration.md — new "Upstream helpers worth knowing" section with the full centroids_from_boundaries → build_distance_weights composition recipe inline, including the (lat, lon) vs (lon, lat) GeoJSON axis-flip.
  • docs/migration-v0-to-v1.md — new "v1.0.1 + v1.0.2 addenda" subsection with before / after snippets for the closed_date SDK / Socrata-bypass migration and the shapely-backed-centroid upgrade.
  • docs/sdk.md!!! tip block in the spatial-weights section pointing at upstream's shapely-backed helper with the complete (lat, lon) adapter snippet for build_distance_weights.
  • docs/integration.md pin row refreshed to the v1.0.2 state (nyc-geo-toolkit>=0.3.0,<0.5).
  • README.md — new v1.0.1 closed_date and v1.0.2 pin-widening subsections under the existing factor-factory integration block.
  • AGENTS.md — new "Current release line" roll-up for v1.0.0 / v1.0.1 / v1.0.2 / v1.0.3.
  • examples/factor-factory-quickstart/ — extended to demonstrate both outcome="complaint_count" and outcome="median_resolution_days" DiD fits against the same panel (the v1.0.1 closed_date pivot workflow). Same panel, single outcome= swap, two ATT recoveries. README gains a "What's also in the panel" table documenting every numeric column the adapter exposes.

Ecosystem convergence

Not code changes in this release, but worth tracking:

  • nyc-geo-toolkit v0.4.1 (2026-04-21) — patch aligning the upstream showcase with jellycell.tearsheets v1.4.0's native API. No public-surface change. Our >=0.3.0,<0.5 pin accepts it automatically.
  • Jellycell v1.4.0 — landed upstream with the native jellycell.tearsheets API (#24) and deps-comma lint fix (#25). All six issues we filed from v1.0.0 dogfooding (J1 through J6) are closed. Our jellycell>=1.3.5,<2 pin accepts v1.4.0 via semver when it hits PyPI. A targeted alignment pass — dropping factor_factory.jellycell.cells.setup() workarounds in favour of the native API — is a future concern gated on factor-factory's own update cycle.
  • Three downstream follow-ups filed: nyc311#24 (this release), subway-access#19 comment, blaise-website#20 (resolution-equity centroid swap).

1.0.2 - 2026-04-20

Patch release. Widens the nyc-geo-toolkit pin so consumers can pick up upstream v0.4.0 (shapely-backed centroids_from_boundaries) alongside nyc311.

Changed

  • Widen the nyc-geo-toolkit pin from >=0.3.0,<0.4 to >=0.3.0,<0.5 so consumers can pick up the just-shipped nyc-geo-toolkit v0.4.0 alongside nyc311. The upstream v0.4.0 release adds a shapely-backed :func:nyc_geo_toolkit.centroids_from_boundaries helper (closing random-walks/nyc-geo-toolkit#12). nyc311's own homegrown nyc311.temporal.centroids_from_boundaries is the shapely-free path and stays as-is with a new .. note:: cross-reference in its docstring explaining when to use which — the two return different shapes (dict vs BoundaryCollection) on purpose.

1.0.1 - 2026-04-20

Patch release. Fixes #20 — resolution-time / SLA analyses no longer have to bypass the SDK.

Added

  • ServiceRequestRecord.closed_date (date | None, default None) — carries the per-complaint resolution-date column through the full ingest / export / dataframe / Socrata pipeline. Fixes #20: bulk_fetch's Socrata $select now requests closed_date alongside created_date, the CSV reader and writer both preserve the column, and records_to_dataframe surfaces it as a datetime64-typed column (with pandas NaT ↔ Python None round-trip). Consumers doing resolution-time / SLA analysis can compute record.closed_date - record.created_date directly instead of bypassing the SDK.

Schema change is additive: existing call sites that instantiate ServiceRequestRecord without closed_date keep working; the default None models the unresolved-complaint case exactly as Socrata returns it. CSV snapshots written by pre-v1.0.1 SDKs load without closed_date too — the CSV column is optional.

1.0.0 - 2026-04-19

First major release. The headline is integration with factor-factory — every PanelDataset can now feed factor-factory's 17 causal-inference engine families without leaving the nyc311 API.

Changed

  • Drop Python 3.10 and 3.11 support. Minimum is now 3.12, to match upstream factor-factory. Existing nyc311 0.3.x consumers on Python 3.10/3.11 should either stay on 0.3 or upgrade Python first. See migration-v0-to-v1.md.
  • Bump nyc-geo-toolkit minimum to >=0.3.0,<0.4 (from >=0.1.7,<0.2.0), to pick up the v0.3.0 modernization pass (Claude Code infra parity, factor-factory/jellycell showcase example, pin bumps). Existing nyc311 consumers of nyc311.geographies and the haversine helpers see no API changes — the upstream bump is additive.
  • CI: bump actions/checkout to v6, setup-uv to v8.1.0 (exact — no moving tag), upload-artifact to v7, keep download-artifact@v8. Add macOS and Windows runners to the tests job matrix.

Added — factor-factory integration

  • PanelDataset.to_factor_factory_panel() — additive adapter returning a factor_factory.tidy.Panel with full treatment-event and spatial-weights round-trip.
  • Pipeline.as_factor_factory_estimate() — thin bridge that dispatches into factor_factory.engines.<family>.estimate on a converted Panel. Supports every factor-factory engine family (did, sdid, rdd, scm, mediation, changepoint, stl, panel_reg, inequality, spatial, reporting_bias, hawkes, survival, event_study, het_te, dml, climate, diffusion).
  • nyc311.temporal.panel_dataset_to_factor_factory and spatial_weights_from_panel as the function-style equivalents.
  • nyc311.factors.dispatch_factor_factory_engine for direct engine dispatch independently of the Pipeline API.

Added — jellycell tearsheets

  • New tearsheets optional extra: pip install nyc311[tearsheets] pulls jellycell>=1.3.5,<2.
  • Both production case studies (examples/case_studies/rat_containerization/, examples/case_studies/resolution_equity/) gained a new tearsheet-generation step that emits manuscripts/{METHODOLOGY,DIAGNOSTICS_CHECKLIST,FINDINGS,MANUSCRIPT,AUDIT}.md alongside the authoritative FINDINGS.md. Numbers are unchanged — the tearsheet is a parallel deliverable.

Added — new case studies

  • examples/sdid-multi-borough-policy/ — self-contained showcase for factor_factory.engines.sdid over a synthetic 5-borough 311 rollout. Runs offline in seconds.
  • examples/mediation-cascade-resolution/ — self-contained showcase for factor_factory.engines.mediation.four_way over a synthetic pilot → triage-time → resolution-rate cascade.

Added — Claude Code infrastructure

  • .claude/agents/release-auditor.md and .claude/agents/factor-compat-auditor.md — read-only auditors for release preflight and factor-factory-bridge drift.
  • .claude/commands/{bump,release-check,run-case-study}.md.
  • .claude/skills/{factor-compat,stats-module-discipline,release-bump}.md.
  • .claude/settings.local.json permissions allowlist, .claude/launch.json dev-server configs.
  • Top-level AGENTS.md (canonical cross-agent-vendor guide — Cursor / Codex / Copilot / Aider / Zed / Windsurf read this), CLAUDE.md (Claude-specific overlay), CONTRIBUTING.md, .github/PULL_REQUEST_TEMPLATE.md, CITATION.cff.

Added — examples showcases

  • examples/factor-factory-quickstart/ — minimal no-jellycell showcase that exercises PanelDataset.to_factor_factory_panel()factor_factory.engines.did.estimate → pandas in ~50 lines. Starting point for consumers who want the adapter without the reporting machinery.

Added — docs

  • docs/integration.md — crosswalk between nyc311 and factor_factory (Panel schema, stats-module map, engine families).
  • docs/migration-v0-to-v1.md — before/after snippets for consumer upgrades.
  • README — new "factor-factory integration" section + links to all four new engine-showcase examples.
  • docs/sdk.md, docs/architecture.md, docs/index.md, docs/releasing.md, docs/getting-started.md, docs/cli.md, docs/contributing.md, docs/examples.md — refreshed to v1.x framing; docs/architecture.md mermaid now shows the two new bridges and the jellycell branch.

Added — previously-unreleased content

The causal-inference, spatial-econometrics, equity, reporting-bias, Bayesian, and point-process statistical methods listed under ## 0.3.0 below shipped in source on main under that tag, but additional polish, docstring cross-references to factor-factory, and case-study wiring land together under v1.0.0.

Changed — examples isolation

  • examples/*/uv.lock and examples/*/.venv/ are gitignored repo-wide. Example reproducibility comes from pinned version ranges in each pyproject.toml, not from committed lockfiles. Four previously-committed uv.lock files (~5,000 lines) were dropped.
  • Hygiene-hook scope aligned with subway-access v0.5 conventions:
  • prettier narrowed to exclude: ^examples/.*\.md$ (markdown-only; example yaml / json / pyproject still get prettier).
  • blacken-docs gained exclude: ^examples/ so showcase-authored Python code blocks in READMEs aren't rewritten to root-project style.
  • Top-level pre-commit exclude: widened to ^(\.cruft\.json|\.copier-answers\.yml|cache/.*|examples/.*/cache/.*|examples/.*/(manuscripts|artifacts)/.*|seeds/enhanced/research/.*)$ — covers runtime caches (including symlinked ones), committed tearsheets/artifacts, and a reserved seeds/ slot for future research notes.
  • Ruff per-file-ignores for examples/**/*.py broadened to the showcase-friendly set (unicode ambiguity, cross-platform shebang, print progress) — protects future examples from surprise lint failures.
  • Per-example .gitignores strip trailing slashes from dir patterns (cache/cache, artifacts/artifacts, etc.). Trailing slash matches real dirs but not symlinks-to-dirs — the subway-access v0.5 engine-audit work surfaced this when a 631 MB cache was symlinked to shared storage and git started tracking it. No-trailing-slash matches both.

Changed — jellycell tearsheet reproducibility

  • The four case studies commit their manuscripts/*.md tearsheets and artifacts/*.json result files so the jellycell site is reproducible from a fresh clone without running the pipeline. factor_factory.jellycell.tearsheets.* is called with template_overrides pinning project to a stable display name and generated_at to a fixed string — committed output is byte-identical across machines.
  • examples/case_studies/resolution_equity/figures/ (3 PNG plots + 4 derived-research CSVs) is now committed rather than gitignored. The resolution-equity study can't be re-run without live Socrata access, so keeping the figures in git means a reviewer can inspect the study's visual output from a fresh clone.

Added — data provenance sidecars

Both production case studies now ship a data/demographics.csv.meta.json sidecar describing the upstream Census ACS tables used (B01003_001E, B02001, B19013_001E, B25003), the vintage year, the exact derivation formulas for the two ratio columns (pct_nonwhite, pct_renter), the log transform on log_median_income, and a how_to_regenerate note for future updates. Matches subway-access v0.5's per-station data.json + research.md provenance convention.

Removed — legacy cruft

  • examples/case_studies/rat_containerization/FINDINGS copy.md (duplicate of FINDINGS.md, byte-identical, leaked into main in an earlier commit).

Known issues

Two upstream factor-factory bugs that the adapter test suite catches and xfails with clear remediation notes. Neither affects the nyc311 adapter path itself — the PanelDataset → Panel conversion is correct in both cases.

  • factor_factory.engines.panel_reg.pyfixest references a 'Coefficient' column that pyfixest>=0.50 no longer emits. Workaround upstream is a column-name update.
  • factor_factory.engines.stl.sktime_stl reads freq from the DataFrame MultiIndex, but pandas doesn't preserve DatetimeIndex.freq on MultiIndex levels after set_index/sort_index. Workaround upstream is to fall back on panel.metadata.freq.

Contracts

v1.0.0 introduces three new public contracts. All are additive and any change to them after this release requires the factor-compat-auditor ceremony:

  • PanelDataset.to_factor_factory_panel(*, outcome_col, provenance, spatial_weights) -> factor_factory.tidy.Panel
  • Pipeline.as_factor_factory_estimate(panel, *, family, method, outcome, **engine_kwargs)
  • Pipeline ↔ factor_factory.engines.* supported-family list (_SUPPORTED_FAMILIES in src/nyc311/factors/_factor_factory.py).

Each of the 11 nyc311.stats modules with a factor-factory equivalent gained a .. note:: block cross-referencing the upstream engine as the preferred backend. The homegrown functions continue to work for backwards compatibility.

0.3.0

  • add composable factor pipeline (nyc311.factors) with seven built-in domain factors (ComplaintVolumeFactor, ResolutionTimeFactor, TopicConcentrationFactor, SeasonalityFactor, AnomalyScoreFactor, ResponseRateFactor, RecurrenceFactor) and an immutable Pipeline builder
  • add temporal panel module (nyc311.temporal) with balanced panel construction, treatment-event modeling, and inverse-distance spatial weights
  • add statistical modeling module (nyc311.stats) with interrupted time series, PELT changepoint detection, STL seasonal decomposition, global and local Moran's I (LISA) spatial autocorrelation, and panel fixed/random-effects regression wrappers
  • add nyc311.pipeline.bulk_fetch() for full-city per-borough downloads with .meta.json integrity sidecars
  • add the resolution-equity case study under examples/case_studies/resolution_equity/, exercising the full v0.3.0 surface against ~1M real records
  • upgrade all model dataclasses to frozen=True, slots=True
  • add the stats optional dependency group (ruptures, linearmodels, esda, libpysal, statsmodels)
  • add mypy overrides for the new optional dependencies
  • align ruff per-file ignores with the example slugs convention so the resolution-equity case study scripts lint cleanly
  • standardize public docstrings on Google-style Args: / Returns: / Raises: so the mkdocstrings-rendered API reference is uniform across the package

0.2.6

  • align the published changelog with the docs refresh that already shipped in 0.2.5
  • keep the public docs, release notes, and package history in sync after the docs-only follow-up patch

0.2.5

  • sharpen the README and core docs around the stable 0.2.x package surface
  • document the nyc311 and nyc-geo-toolkit relationship more clearly across user-facing and maintainer docs
  • refresh install, contributor, and release guidance to match the current workflow

0.2.4

  • delegate duplicated geography conversion and boundary-loading helpers back to nyc-geo-toolkit
  • add CI coverage against nyc-geo-toolkit on main so downstream breakage is caught before release
  • stabilize compatibility and version checks across local CI, release builds, and the toolkit-main validation path

0.2.3

  • refresh public authorship metadata to credit Blaise Albis-Burdige directly
  • add blaiseab.com as the portfolio link on package and docs surfaces
  • align README, docs, and site metadata with the same attribution model

0.2.2

  • migrate reusable geography ownership to nyc-geo-toolkit
  • preserve the public nyc311.geographies API through compatibility adapters
  • remove duplicated bundled boundary assets from nyc311 and depend on the published toolkit package instead

0.2.1

  • polish the README and package metadata for a cleaner PyPI project page
  • align docs wording with the shipped stable 0.2.x line
  • keep the release workflow current with the validated TestPyPI then PyPI path

0.2.0

  • ship the first public stable release in the 0.2 line
  • include topic coverage, resolution gaps, anomaly detection, report-card export, dataframe helpers, and the refreshed uv extras/groups and CI setup

Earlier History

  • 0.2.0a1: internal milestone that retired the older v0.1 framing in favor of an explicit 0.2 release line

Release history

nyc311 tracks release history through GitHub Releases and tags.

GitHub Releases remains the authoritative public record of shipped artifacts.