City digital twin · Part 7 · Two cities

Two cities, one framework: Mumbai, and the agents that built it

Newcastle was the first city. The second is Mumbai: 27 million synthetic residents, its suburban trains taken from the printed timetables, its metro, buses and ferries, all running on the same framework, stood up in days. This post shows it, and what made it possible: fifty days of building with AI coding agents that kept the whole project in the repository rather than in anyone's head.

· ~8 min read · Part 7 of a series · with Pranav Dhoolia

A second city

Previously I showed a working twin of Greater Newcastle, six hundred thousand people. Mumbai is more than forty times larger and different in almost every way that matters to a travel model: the suburban railway carries millions a day, auto-rickshaws are a mode of their own, and much of the data lives in printed timetables and government portals rather than open feeds.

The twin's 3D viewer over central Mumbai, from Mahim Bay across Dadar, Matunga and Wadala towards Kurla and the Bandra-Kurla Complex. The suburban railway lines run in purple through the middle of the city, the Rajiv Gandhi Sea Link crosses the bay, and the streets are coloured by simulated delay, heavily red.
FIG 1 - Central Mumbai in the same viewer as Newcastle: the Western and Central Railway lines in purple through Dadar and Matunga, the Sea Link across Mahim Bay, and every street coloured by simulated delay. Why it is mostly red is explained below.

The Mumbai twin covers the core of the Mumbai Metropolitan Region. Its 27,057,132 synthetic residents live in 6.1 million households, generated from the 2011 census and carried forward to 2026 with vehicle registrations and population projections. It runs on the same code as Newcastle: the same simulator, the same viewer, the same checks, the same scoreboard.

Mumbai, rebuilt from its own data

Map of the Mumbai twin's network: about 820,000 road links in grey and about 73,000 footpath and cycle links in green, covering the island city, the western and eastern suburbs, Thane, Navi Mumbai and the districts beyond.
FIG 2 - Mumbai's network: 824,536 road links and 72,809 footpath and cycle links from OpenStreetMap, with gradients from a global elevation model.

The suburban railway carries most of the city's longer trips, and it has no open timetable feed. So the twin reads the Western and Central Railways' own printed timetables: 3,289 trains, against the 3,234 services the operators publish, each stopping where the sheet says it stops. The metro lines run at their published headways, the buses from a community-built feed and the regional operators' data, and seven commuter ferry crossings from the Maritime Board's directory. Trains, metro cars and boats carry the capacities their operators publish.

Map of every timetabled service in the Mumbai twin: suburban and regional train lines in blue running north along the western coast and north-east through Thane and Kalyan, metro lines in orange, bus routes in green and ferry crossings across the harbour.
FIG 3 - Every timetabled service: 1,964 train patterns from the printed timetables and regional services, 1,802 bus route patterns, the metro lines and the ferry crossings.
Hexagon density map of where the Mumbai twin's synthetic residents live, densest in the island city and the western and central suburbs, then Thane and Navi Mumbai, thinning towards the outer districts.
FIG 4 - Where the synthetic residents live, from a 1% sample of them: the island city and the suburbs along the railway lines, then Thane and Navi Mumbai.

Many Indian government websites refuse connections from an Australian address, so sources came through Internet Archive copies, a VPN endpoint in India, and - for data behind a login and a captcha - downloads from a browser, imported by script with a record of where each came from. Of 600 catalogued sources, 572 were acquired, each with a stated use in the model.

Where Mumbai stands

Mumbai has not produced a ridership reading yet, for the reason this run shows.

The viewer showing a finished 1 percent Mumbai run: the whole region's 379,019 loaded links coloured almost entirely red, and on the left the run's card warning that at 1 percent MATSim floors link storage at one vehicle, so the run produces spurious spillback, above the table of modes against their targets.
FIG 5 - A finished 1% run of Mumbai: the whole region red. The viewer's own card says why - at a 1% sample every road holds so little that the city jams whatever the model does.

A simulation that runs a fraction of the population also scales every road down by that fraction. At 1%, a typical Mumbai street can pass one vehicle every few minutes, and on a network whose median link is 65 metres long the city gridlocks no matter how much memory the machine has. The smallest sample that behaves is about 10%: 2.7 million agents and roughly 247 GB of working memory, several times what the workstation that built it has. The framework carried Mumbai all the way to a running simulation. It needs a larger machine to go further.

One framework, any city

Mumbai took days because the framework was made city-agnostic from the start. In its first week, 338 file paths across 46 scripts assumed Newcastle was the only city; they were moved behind one module that knows where a city lives, and the rule became that the code may not name a place. A city is a folder that supplies a described contract, and a throwaway test city with a different projection, seed and set of modes proves on every change that the framework reads the same for a city it has never seen.

FRAMEWORK CITIES simulation code · schema tests · the run command names no place, no coordinate, no extent a throwaway test city with a different projection, seed and modes must run it unchanged checked in CI on every change the contract refuses a city missing anything cities/newcastle derived boundary · 600 values 968 hashed files · adapters its own documents cities/mumbai derived boundary · 428 values 1,459 hashed files · adapters its own documents differences between cities are declared switches, never a branch on a city's name
FIG 6 - What "a city" is to the framework. Newcastle and Mumbai are two folders; the code between them does not know either name.

Built with agents

Both cities were built between 10 August and 30 September in 64 working sessions with AI coding agents - 709 commits, reviewed and steered by us, written mostly by them. The main difficulty was continuity: each session starts with no memory of the one before.

Previously I wrote an agent that onboards a repository the way a new engineer would, building itself a table of contents. This project turns that around: the repository is kept as the table of contents, so any session can read its way to the current state in about 600 lines.

WHAT A SESSION WITH NO MEMORY READS, IN ORDER 1 · THE GOAL one page · what the twin is for 2 · THE BOARD one page · generated from run records 3 · THE BRIEF expiring facts + the command for each 4 · ONE POSITION PAGE ≤ 130 lines 5 · ONE RECORD SECTION via its index about 600 lines in total; the 18,000-line record is never read whole
FIG 7 - What a session with no memory reads, in order. The goal, the status board, a short brief, one page on the topic at hand, and one section of the full record.

Three habits kept fifty days of agent work honest. Every fact has one home, and the pages that repeat it are generated from it or checked against it. Every mistake that happens twice becomes a check that refuses it - "a rule a session can skip is a wish", as one of the project's hooks puts it - so a typed-in map rectangle, a script that silently corrupted files, a forced Windows restart that killed a 28-hour run, each now has something that stops it. And the decisions and the spending stay with us: open questions arrive once, as clickable choices with a recommendation, and no day-long run starts without an approved, measured cost.

What came next

A twin that could stand up a city of 27 million people on one contract caught the attention of a state government in India, planning a new city on Mumbai's edge. The proposal we prepared for it is the next post.

Sources & anchors

  1. Figures 1 and 5: the twin's run viewer on the Mumbai run 20260922T005949_4it_1pct · figures 2 to 4: drawn from Mumbai's assembled network, weekday timetables and that run's persons · map data © OpenStreetMap contributors (ODbL), buildings from Overture Maps
  2. Mumbai in the twin - cities/mumbai/docs/README.md · the suburban timetable builder - build_suburban_timetable_feed.py
  3. The city contract - src/city.py · city.schema.json · the throwaway-city test - check_city_agnostic.py
  4. How the sessions work - HANDOVER_CONTRACT.md · the onboard and handoff skills - onboard, handoff · the hook quoted - block-stdin-trap.sh
  5. Claude Code - github.com/anthropics/claude-code

City digital twin · Part 7 of the series · ← Inside the Newcastle twin The City, Twice →