Inspiration
A lot of software stores facts about places, such as which town an address is in, which flood zone, which school district. Permit systems, mortgage tools, school enrollment and 911 dispatch all do it. Those facts come from government data, and that data changes without notice. This month the Census gave South Orange, NJ a new identifier, so any app that stored the old one is now wrong and doesn't know it. FEMA has a pending flood map that would put a county park into a mandatory flood insurance zone. NJDEP's contaminated sites list changes 34 to 111 times a month. None of these sources version their data, publish a changelog, or tell the consumers that need up-to-date answers.
Code dependencies solved this years ago with semver, lockfiles and bots that open upgrade PRs. Data never got that. Packaging the data is the easy part. Keeping it current is the actual work, and more often than not it just rots.
What it does
Agrimensor is a registry of versioned geospatial packages, maintained by GitLab Duo agents.
- Each source is one YAML spec. Its format, whether its features have stable IDs, its geometry (containment or proximity), and its semver rules. The engine knows formats, and nothing is hardcoded to a source.
- For the hackathon, three real New Jersey packages are available today: Census civic boundaries, FEMA flood zones for Ocean County, and NJDEP's Known Contaminated Sites.
- A release is classified, not just a simple diff. Mergers/recodes/boundary changes are named events with ID crosswalks, separated from the Census's yearly redrawing noise. Retiring an ID is classified as a major release; a boundary moving is minor; noise is a patch.
- Consumers pin packages in a lockfile and register their records.
agrimensor checktells you which records a release changed, why, and what to do.
The lifecycle after the code is the product:
- A scheduled watch job checks every source and goes red when one changes, opening a sync issue.
- The issue starts a custom Duo flow that fetches the new data, surveys where land moved, classifies events, writes the changelog and migrations, derives the semver verdict and opens a merge request.
- Duo Code Review checks it against repo-specific rules, and SAST and secret detection scan it.
- Non-breaking releases merge themselves. A release that retires IDs opens as a draft labeled
human-approval, and a person makes one judgment call. - CI tags the release, rebuilds the data from source, refuses to publish anything that doesn't reproduce the committed snapshots, and publishes versioned artifacts with checksums, citations and migration files.
- Downstream, the consumer's own scheduled job goes red when its pin falls behind, and its own Duo flow opens a merge request rewriting its stored IDs through the crosswalk.
The video shows this running live on TIGER2026 (from earlier in October). The watch job caught it, the agent shipped v4.0.0 retiring four IDs (South Orange, Henry Hudson Regional, and the Greenwich/Stow Creek school district merger), and a permit router downstream got a one-file migration MR. My part was one merge.
How we built it
- Python engine (geopandas, shapely, pyarrow, pydantic, click) with format adapters for zipped shapefiles and ArcGIS feature services.
- Two custom Duo flows as flow-registry YAML in
.gitlab/duo/flows/: vintage sync and consumer migrate. They followAGENTS.md, which is the operating manual for every agent in the repo (procedure, scope, network allowlist, and the human gate). - Foundational flows: Code Review with custom instructions, and Fix CI/CD Pipeline.
- GitLab CI for lint, a 360-test geometric regression suite, SAST, secret detection, automerge, tagging, packaging and releases to the GitLab package registry.
- A small FastAPI + MapLibre app that shows every release on a map, deployed to Google Cloud Run.
Challenges we ran into
- Telling real change from noise. The Census slightly redraws lines nearly every year. One year, West Milford "moved" 0.67 km² from redrawing alone, while a genuine transfer between Eatontown and Oceanport was 0.15 km². A plain geometry diff cannot separate those, so each layer has calibration thresholds, and the agent isn't allowed to touch them.
- Sources with and without identity. Towns have stable IDs, so a merger means stored IDs must migrate. Flood zones don't, so only the answer at a point changes. That one
identityflag in the spec decides which kind of release you get.
Accomplishments that we're proud of
- The full loop runs live, on real data the pipeline had never shipped, from a red watch job to a merged migration in a downstream repo.
- Adding a source is writing one YAML file, not a new pipeline. The engine is fully general.
- Every release is reproducible. CI rebuilds it from source and refuses to publish if it doesn't match.
What we learned
The agent is most useful when its rulebook is strict. Clear scope (which files a sync touches), clear gates (what needs a human), and checks it can't argue with (tests, reproducibility) are what make unsupervised merges safe.
What's next for Agrimensor
More sources and states, expanding the range of supported formats, and an installable consumer flow so any consumer repo can trivially opt in.
Built With
- click
- docker
- fastapi
- geopandas
- gitlab
- gitlab-ci
- gitlab-duo
- gitlab-duo-agent-platform
- gitlab-package-registry
- google-cloud-run
- maplibre-gl
- pyarrow
- pydantic
- python
- shapely
- typescript
- vite
Log in or sign up for Devpost to join the conversation.