# The City Charter

Version 1.0. This is the architecture of the city: every law that turns a repository into streets and buildings, why each law exists, and how to add new ones. Anyone changing how the city looks or behaves should start here.

The tables between `<!-- table: ... -->` markers are generated from the code by `npm run charter`, so they cannot drift from what the app does. Everything else is written by hand.

## 1. How a repository becomes a city

```
 GitHub archive                 server/                          shared/ (runs in the browser)                public/js/
┌──────────────┐  stream  ┌─────────────────┐   JSON    ┌──────────────┐  spec  ┌────────────┐  plan  ┌──────────────┐
│ owner/repo   │ ───────▶ │ ingest + dna    │ ────────▶ │ rulebook     │ ─────▶ │ plan       │ ─────▶ │ geometry,    │
│ .tar.gz      │          │ measures files, │           │ designs each │        │ lays out   │        │ facades,     │
└──────────────┘          │ keeps no text   │           │ building     │        │ the grid   │        │ simulation   │
                          └─────────────────┘           └──────────────┘        └────────────┘        └──────────────┘
```

| Stage | Code | Input | Output |
|---|---|---|---|
| Ingest | `server/ingest.js` | Repository archive | One record per file: path, size, address, language, DNA |
| DNA | `server/dna.js` | First 192 KB of a file | Numbers that describe the text (part 3) |
| Rulebook | `shared/rulebook/index.js` | File record | Building specification, with the id of every rule that fired |
| Plan | `shared/plan.js` | All specifications | Avenues, streets, lots, junctions, parks |
| Pattern book | `public/js/world/archetypes.js` | Specification | Geometry for one building |
| Facade shader | `public/js/world/facade.js` | Wall surfaces | Materials and windows, per pixel |
| Simulation | `public/js/sim/` | Plan | Traffic, citizens, events |

The server never stores file text or sends it with the city. Three small things do travel with it: `word`, a single frequent word used as a building's name; the names each file defines, up to 60 a file and 60,000 a city, which the find tool searches; and the README. Going inside a building fetches that one file's text at that moment.

Same input, same city. All randomness comes from seeded generators in `shared/rng.js`. The lot is seeded by the path and the details by the file's contents, so editing a file changes its building without moving it.

## 2. Laws of the city plan

These are the fixed laws. New rules can decorate them but must not break them. Tests in `test/plan.test.js` and `test/rulebook.test.js` enforce them.

**Law 1. A building is a file.** Every file gets exactly one building. Nothing else in the city is a building.

**Law 2. A street is a directory.** Every directory gets an east-west street, even one with no files of its own. A directory's files stand on its street. The repository root is Root Boulevard, along the south edge. Terminus Road closes the north edge so that no avenue is a dead end.

**Law 3. An avenue is a character.** North-south avenues are characters: letters, digits and symbols. Case is ignored. They run west to east in this order: `. - _ @ $ ~ + #`, then digits, then letters, then anything else.

**Law 4. A directory crosses the avenue of its first character.** The street for `src/utils` crosses U Avenue, since its name starts with `u`. This crossing is the street's home intersection, marked with a roundel in the road. Going north along an avenue, directories are in alphabetical order.

When one character has more directories than an avenue can hold, it gets parallel avenues: S, S 2, S 3. They are filled in alphabetical order. Letters with no directories stay on the map when the city needs the width.

**Law 5. An address is the SHA-256 of the full path, file name included.** The hash of `src/utils/date.js` is the building's street address. The first eight hex digits are shown as the house number, and the full hash is in the file card. An address never changes unless the file moves.

**Law 6. Addresses are in order.** Along a street, each side runs in ascending address order from west to east. The last hex digit picks the side, as odd and even numbers do on a real street: odd is north, even is south.

**Law 7. Height is file size.** This law has no exceptions. A larger file is always a taller building, and content can never change that.

```
floors = 1 + 0.34 x (log2(size / 256))^2
height = 3.4 x floors + 1.2 meters
```

| File size | Floors | Height |
|---|---|---|
| 1 KB | 2 | 9 m |
| 4 KB | 6 | 23 m |
| 16 KB | 13 | 46 m |
| 64 KB | 23 | 79 m |
| 256 KB | 35 | 120 m |
| 1 MB | 50 | 171 m |
| 16 MB | 88 | 300 m |

**Law 8. Content decides everything else.** Form, wall, windows, roof and ornament come from the file's text, by way of the rules below.

### Filling the grid

The planner tries several block sizes and row counts and keeps the most compact layout. Each block is then shrunk to fit its lots. A street spans whole blocks, always ending at an avenue, so traffic never meets a dead end. A street too long for the city continues on a later row as an extension (`src Ext. 2`).

Space left over becomes a park, a plaza or a parking lot. A grid cell with no street at all becomes woodland.

### Street arrangements

The planner always works out one logical grid, and a layout then decides what it looks like on the ground. Radial, spiral, crescent and zigzag bend that grid into another form. Each form only ever stretches the plan, which is why lots that cleared each other on the grid still clear each other. Addresses never change.

Boroughs and archipelago plan each root folder as a city of its own (`shared/districts.js`), in a layout chosen by its size or by the script, then join them with highways or bridges. A folder of fewer than three files stays Downtown. Column and row numbers are shifted so every junction still has one name.

A roundabout or plaza is a junction opened into a loop of short ordinary roads, one stop where each road arrives (`shared/junctions.js`). Cars, walkers, courses and the map use it without knowing it is special. A loop only opens where the buildings leave room, because nothing moves once it has an address.

### Elevation

Hills are only drawn. The height map (`shared/terrain.js`) is read by the shaders, which lift every vertex of every material just before the camera sees it, and the camera stays in flat space. The collider, the courses, the road graph and every player remain flat, so elevation can be on for one player and off for another in the same room, and it changes no race or game of tag. Picking and screen labels add the same lift, so what you click is what you see.

Each lot gives the ground a value, by default how often its file changed in the last few hundred commits. Busy files pull harder, so hilltops are theirs. The field is blurred into land, shaped, faded to nothing at the shore, and given a level pad under each building. Change counts come from a clone with history and no file contents, so they use no GitHub API calls. If `git` or GitHub cannot answer, the city still builds and its hills follow file size, and the setup dialog says so.

### Roads on hills

A road cannot simply follow a hillside, so `surveyRoads` gives each one its own level. It starts from the land along the road, smooths it, and holds it to the steepest allowed grade (`grade` in a terrain block, 0.14 by default). Where that leaves the road below the land it runs in a cutting, held back by a retaining wall. Where it leaves it above, it stands on a bridge with piers, and the bare land carries on beneath: the land is drawn from its own height map, separate from the one that lifts roads and everything on them. Buildings stand close to the level of the road they front. All of this is rebuilt when the land changes, and `roadworks: false` turns it off.

The minimap draws contour lines from the same map.

### Road dimensions

Set in `shared/constants.js`. Streets are 11 m wide, avenues are 15 m, and sidewalks are 3.2 m.

### City styles

The plan above is the logical city. A style (`shared/shape.js`) decides how it sits on the ground, and the plan's laws still hold in every style: a directory is a street, a character is an avenue, a file keeps its address, and a larger file is never shorter than a smaller one.

| Style | Land | Roads | Heights |
|---|---|---|---|
| Planned grid | Flat and straight | Every street and avenue | The height law as written |
| Metropolis | Gentle sweeps, tight spacing | Every road | Larger footprints, at least 17 floors, height law times 2.4 |
| Suburbs | Curving, 1.4 times the spacing | Roads with no files are dropped, so dead ends become cul-de-sacs | Height law times 0.26 |
| Countryside | Winding, 2 times the spacing | Sparse lanes between fields | Height law times 0.12 |

The README is read after the style is applied, so its billboards and monument land on the ground that style made.

A style works in four steps: bend the land with a smooth function that can never fold over itself, prune roads while keeping them connected, turn each lot to face its stretch of road, and turn empty ground into what suits the place (plazas, yards, woods or fields). Every step uses a generator seeded from the repository and a variant number, so **Generate a new layout** gives a different arrangement of the same city, and anyone with the same style and variant gets exactly the same one. Players only share a public room if they share a layout. In a private room the host's choice moves everyone.

## 3. File DNA

Every field that the rules can read. `m` values in the rulebook are the same numbers scaled to roughly 0 to 1, so conditions stay readable.

| Field | Meaning | Reads as `m` |
|---|---|---|
| `hash` | Fingerprint of the contents, seeds the details | |
| `entropy` | Byte entropy from 0 to 1 | |
| `binary` | 1 if the file is not text | |
| `lines` | Line count, scaled up if the file was sampled | |
| `blank` | Share of lines that are blank | `m.blank` |
| `comment` | Share of non-blank lines that are comments | `m.cm` |
| `avgLen`, `maxLen` | Mean and longest line length | `m.long` |
| `indent`, `indentMax` | Mean and deepest indentation | `m.ind` |
| `tabs` | Share of lines indented with tabs | |
| `funcs` | Functions per 1,000 lines | `m.fn` |
| `classes` | Types and classes per 1,000 lines | `m.cl` |
| `imports` | Import lines per 1,000 lines | `m.im` |
| `branches` | Conditionals and loops per 1,000 lines | `m.br` |
| `asyncs` | Concurrency words per 1,000 lines | `m.as` |
| `errors` | Error handling words per 1,000 lines | `m.er` |
| `tests` | Test assertions per 1,000 lines | |
| `todos`, `fixmes` | Count of TODO and FIXME style markers | |
| `urls` | Count of web addresses | |
| `upper`, `digit`, `punct` | Share of each kind of character | `m.up`, `m.punct` |
| `minified`, `generated`, `shebang` | Flags | |
| `word` | Most frequent meaningful word, used to name the building | |
| `color` | Most used vivid hex color in the file | |
| `signature` | Twelve digits: mean line length across twelve slices of the file | |

`signature` is the file's rhythm. Stepped forms (`ziggurat`, `deco`, `cluster`, `pagoda`) carve it into their setbacks, so the outline of those towers comes from the shape of the text.

## 4. Height tiers

| Tier | Floors | Typical file | Forms available |
|---|---|---|---|
| 1 | Under 2 | Under 0.8 KB | Bungalows, cabins, small shops |
| 2 | 2 to 3 | Up to 2 KB | Town houses, duplexes, corner stores |
| 3 | 4 to 9 | Up to 9 KB | Apartments, offices |
| 4 | 10 to 24 | Up to 86 KB | Towers |
| 5 | 25 and up | 86 KB and larger | Skyscrapers |

## 5. The rules

Rules run in stages, in this order. A **first** stage stops at the first match. An **all** stage runs every match. The ids of the rules that fired are stored on the building and shown in its file card under "Why it looks like this".

### Stage 1: Zoning (first match)

<!-- table: zoning -->
| Id | Rule |
|---|---|
| Z-01 | A root README is City Hall |
| Z-02 | A license is the Courthouse |
| Z-03 | Community documents are chapels |
| Z-04 | A changelog is a clock tower |
| Z-05 | Project manifests are the exchange |
| Z-06 | Lockfiles are grain silos |
| Z-07 | Container files are the port |
| Z-08 | Build files are factories |
| Z-09 | CI pipelines are power stations |
| Z-10 | Empty files are vacant lots with a kiosk |
| Z-11 | Dotfiles are gatehouses |
| Z-12 | Tests are schools |
| Z-13 | Notebooks are observatories |
| Z-14 | Images and fonts are galleries |
| Z-15 | Audio and video are theaters |
| Z-16 | Binaries and archives are data centers |
| Z-17 | Minified and generated files are data centers |
| Z-18 | SQL is the bank |
| Z-19 | Data files are warehouses |
| Z-20 | Stylesheets are boutiques |
| Z-21 | Markup is the department store |
| Z-22 | Long documents are libraries |
| Z-24 | Scripts are workshops |
| Z-99 | Everything else is chosen from the form tables |
<!-- /table -->

### Stage 2: Forms

Files that reach `Z-99` pick a form from their tier's table. Each form scores itself from the file's measures, a little seeded noise is added, and the highest score wins. To favor a form for some kind of file, change its score.

<!-- table: forms -->
| Tier | Forms |
|---|---|
| 1 | `bungalow`, `cottage`, `aframe`, `ranch`, `shop`, `diner` |
| 2 | `townhouse`, `cottage`, `duplex`, `ranch`, `shop`, `corner` |
| 3 | `walkup`, `slab`, `courtyard`, `brutalist`, `ziggurat`, `office`, `glassbox` |
| 4 | `office`, `glassbox`, `hotel`, `twin`, `deco`, `cluster`, `brutalist`, `rotunda` |
| 5 | `deco`, `glassbox`, `twist`, `needle`, `diagrid`, `pagoda`, `cluster`, `crown` |
<!-- /table -->

### Stage 3: Skin (all matches)

<!-- table: skin -->
| Id | Rule |
|---|---|
| S-01 | Language sets the wall material |
| S-02 | Houses are never glass |
| S-03 | The most used color in a file becomes the trim |
| S-04 | Civic buildings are faced in marble |
| S-05 | Heavy industry is clad in metal |
| S-06 | High contrast text hardens the walls |
| S-07 | Technical debt weathers a building |
<!-- /table -->

### Stage 4: Windows (first match)

Then two sizing rules always run. Window bays widen with average line length, because long lines need wide rooms. Occupancy falls as blank lines rise, because blank lines are empty rooms that stay dark at night.

<!-- table: windows -->
| Id | Rule |
|---|---|
| W-01 | Binary files have no windows |
| W-02 | Minified code has arrow slits |
| W-03 | Glass walls take a curtain wall |
| W-04 | Well commented files get arched windows |
| W-05 | Long lines become ribbon windows |
| W-06 | Deep nesting gets portholes |
| W-07 | Industry has loft windows |
| W-99 | Everything else has a regular grid |
<!-- /table -->

### Stage 5: Roof (first match)

<!-- table: roofs -->
| Id | Rule |
|---|---|
| R-01 | Tall buildings get a crown from their branching |
| R-02 | Well documented towers get a spire |
| R-03 | Remaining towers end in mechanical floors |
| R-04 | Class-heavy houses get a hip roof |
| R-05 | Import-heavy houses get a mansard |
| R-06 | Function-heavy houses get a gable |
| R-99 | Mid-rise buildings keep a parapet |
<!-- /table -->

### Stage 6: Ornament (all matches)

<!-- table: ornament -->
| Id | Rule |
|---|---|
| O-01 | Open TODOs put a crane on site |
| O-02 | FIXME and HACK put up scaffolding |
| O-03 | Functions become rooftop machinery |
| O-04 | Imports fill a water tank |
| O-05 | URLs raise antennas |
| O-06 | Comments pay for solar panels |
| O-07 | Blank lines leave room for a roof garden |
| O-08 | The biggest classes earn a helipad |
| O-09 | Shouting text rents a billboard |
| O-10 | A color in the file lights a neon band |
| O-11 | Error handling keeps a chimney burning |
| O-12 | Exports open an awning |
| O-13 | Comment-rich apartments have balconies |
| O-14 | Branch-heavy walk-ups need fire escapes |
| O-15 | Houses have a yard |
| O-16 | Async code puts a pool on the hotel |
| O-17 | Files with tests fly a flag |
| O-18 | Tall towers carry an aviation beacon |
| O-19 | Busy retail adds a rooftop sign |
<!-- /table -->

### Stage 7: Context

These run after planning, when corners and neighbors are known.

<!-- table: context -->
| Id | Rule |
|---|---|
| C-01 | Corner lots open a storefront at ground level |
| C-02 | Buildings on the home intersection fly the avenue colors |
<!-- /table -->

### The README

Every rule above reads one file. These read the repository's README, and they change the city as a whole. They live in `README_RULES` in `shared/readme.js`, run in order, and use the same permanent ids and the same shape as the building rules: a `when` that reads the parsed README and a `then` that records an effect for the world to draw.

<!-- table: readme -->
| Id | Rule |
|---|---|
| R-01 | The first heading is the name on the city limits |
| R-02 | The opening sentence is the city motto |
| R-03 | The main topic picks the city colors |
| R-04 | Each section raises a billboard |
| R-05 | Each image goes up on a billboard of its own |
| R-06 | Files the README names fly a flag and carry a beacon |
| R-07 | A longer README raises a taller monument |
| R-08 | Badges hang bunting across the busiest streets |
| R-09 | A section named after a folder lines its street with banners |
| R-10 | Code samples turn the monument into a terminal |
<!-- /table -->

The topic comes from counting words in the README, with words near the top worth three times as much. It sets the colors of the banners, bunting, billboards and monument.

<!-- table: themes -->
| Theme | Color | Words that lead to it |
|---|---|---|
| Web | `#2f7fe0` | http, https, server, api, web, browser |
| Data | `#7a3fc0` | data, database, sql, query, analytics, dataset |
| Systems | `#c2573f` | kernel, compiler, memory, performance, thread, runtime |
| Tools | `#0f6b52` | cli, terminal, command, tool, build, plugin |
| Publishing | `#a87a00` | documentation, book, guide, tutorial, markdown, docs |
| Intelligence | `#c23b8f` | model, learning, neural, llm, training, inference |
| Play | `#e0651f` | game, engine, render, graphics, shader, physics |
| Security | `#23272e` | security, encrypt, auth, crypto, vulnerability, password |
<!-- /table -->

A billboard is not a building, so Law 1 still holds: it has no address and does not count toward the population. Billboards stand on open ground near the middle of town, facing the nearest road, and the monument takes the most central spot. Each board links back to the line of the README it came from. A README's badges are counted rather than shown, and a section called Contents is skipped.

Pictures come through `/api/readme-image`, which serves only images named by the README of a city this server has built, from public HTTPS addresses or the repository itself, so it cannot be used as a general proxy or to reach a private network.

### Pictures around town

Every picture in the city comes from the repository: the images its README shows first, then the images in its own folders, chosen by `pickRepoImages` in `shared/art.js`, which skips icons and build output. They hang where a real city would hang them: as murals on side walls, on rooftop signs, on bus shelters, on the backs of benches and along the sides of buses. All 20 share one sheet, so together they cost one draw call. A place with no picture to show holds a poster set from the city's own names, so a repository with no images still has street advertising.

Lit panels stay bright after dark, and murals fall into the night with the wall they are on.

## 6. Pattern book

Every archetype, with its footprint in meters before the file's line length adjusts it.

<!-- table: archetypes -->
| Archetype | Width | Depth |
|---|---|---|
| `bungalow` | 14 | 11 |
| `cottage` | 10 | 10 |
| `aframe` | 10 | 12 |
| `ranch` | 18 | 11 |
| `shop` | 10 | 11 |
| `diner` | 15 | 9 |
| `townhouse` | 8 | 13 |
| `duplex` | 13 | 11 |
| `corner` | 12 | 12 |
| `walkup` | 15 | 15 |
| `slab` | 26 | 11 |
| `courtyard` | 24 | 20 |
| `brutalist` | 20 | 18 |
| `ziggurat` | 24 | 24 |
| `office` | 20 | 18 |
| `glassbox` | 20 | 20 |
| `hotel` | 24 | 15 |
| `twin` | 30 | 16 |
| `deco` | 24 | 24 |
| `cluster` | 24 | 24 |
| `rotunda` | 22 | 22 |
| `twist` | 22 | 22 |
| `needle` | 18 | 18 |
| `diagrid` | 24 | 24 |
| `pagoda` | 22 | 22 |
| `crown` | 24 | 24 |
| `cityhall` | 38 | 30 |
| `courthouse` | 30 | 24 |
| `chapel` | 13 | 20 |
| `clocktower` | 13 | 13 |
| `exchange` | 26 | 20 |
| `silo` | 18 | 18 |
| `port` | 30 | 20 |
| `factory` | 30 | 22 |
| `powerplant` | 32 | 26 |
| `kiosk` | 6 | 6 |
| `gatehouse` | 7 | 7 |
| `school` | 30 | 18 |
| `observatory` | 18 | 18 |
| `gallery` | 20 | 18 |
| `theater` | 22 | 24 |
| `datacenter` | 24 | 20 |
| `bank` | 20 | 18 |
| `warehouse` | 24 | 16 |
| `boutique` | 10 | 11 |
| `department` | 24 | 20 |
| `library` | 24 | 20 |
| `workshop` | 14 | 14 |
<!-- /table -->

## 7. Materials register

Each language sets the look of its neighborhood. `Low` is the wall material up to nine floors and `High` is the material above that. `Map` is its color on the minimap and in the language lens.

<!-- table: languages -->
| Language | Low | High | Wall | Map |
|---|---|---|---|---|
| JavaScript | stucco | stone | `#d8b45c` | `#e8c547` |
| TypeScript | brick | glass | `#5a88b8` | `#4b8fd1` |
| Python | stone | glass | `#6f9c8c` | `#4fae94` |
| Ruby | brick | terracotta | `#96303a` | `#d0404f` |
| Go | concrete | glass | `#79c2d0` | `#4dc4dc` |
| Rust | corten | corten | `#a85a30` | `#c8693a` |
| Java | brick | terracotta | `#a9583a` | `#c76b3f` |
| Kotlin | terracotta | glass | `#8d6cc0` | `#9b78d6` |
| Scala | brick | stone | `#b6433f` | `#d4534e` |
| C | concrete | concrete | `#8f969b` | `#9aa5ad` |
| C++ | stone | metal | `#667289` | `#7183a6` |
| C# | brick | glass | `#6f4f9a` | `#8a63c2` |
| Swift | terracotta | glass | `#e27446` | `#f0784a` |
| Objective-C | stucco | stone | `#b8a48e` | `#b8a48e` |
| PHP | stucco | glass | `#8288bd` | `#8b92d4` |
| Shell | metal | metal | `#6d9a58` | `#74b857` |
| PowerShell | metal | metal | `#4c79ad` | `#4c8ad0` |
| Lua | stucco | glass | `#4659a8` | `#5a70d0` |
| Elixir | stucco | glass | `#7d5c9c` | `#8e6bb3` |
| Haskell | marble | stone | `#a69cbd` | `#a69cbd` |
| Dart | stucco | glass | `#56b8cf` | `#56b8cf` |
| Vue | wood | glass | `#66b78c` | `#49c08a` |
| Svelte | terracotta | glass | `#e8693a` | `#f26b35` |
| HTML | terracotta | stone | `#d86b3f` | `#e6703f` |
| CSS | stucco | glass | `#d994b6` | `#ec8fbf` |
| SVG | stucco | glass | `#e3a34c` | `#f0ae4f` |
| Markdown | stone | stone | `#d8c9a8` | `#dcc99a` |
| Docs | marble | stone | `#e3dccb` | `#e6dfca` |
| JSON | stucco | concrete | `#e8e4d8` | `#d9d6c8` |
| YAML | wood | metal | `#b48c5c` | `#c49a62` |
| TOML | wood | metal | `#a97b4e` | `#b98a58` |
| Config | concrete | metal | `#a3a79f` | `#a7ab9f` |
| XML | stucco | concrete | `#c9b98f` | `#cdbb8a` |
| SQL | marble | stone | `#dcd4bc` | `#d6b84f` |
| Data | metal | metal | `#7f8c94` | `#88a0ae` |
| Notebook | marble | glass | `#e6a85c` | `#ef9f3e` |
| Docker | metal | metal | `#3c8fd6` | `#3c9be6` |
| Make | brick | metal | `#7a6a5a` | `#8f7c68` |
| Terraform | concrete | glass | `#7a5fc9` | `#8468d8` |
| Image | marble | glass | `#eae4d6` | `#eae4d6` |
| Media | concrete | glass | `#c4525c` | `#d9606a` |
| Font | marble | stone | `#d2cbd8` | `#cfc6d8` |
| Archive | metal | metal | `#8a7f6c` | `#94886f` |
| Binary | metal | metal | `#5b626c` | `#6c7480` |
| Other | stucco | concrete | `#b6b0a4` | `#a8a498` |
<!-- /table -->

Wall materials are procedural, in `wallPattern` in `facade.js`: plain, brick, stucco, concrete, glass, metal, wood, stone, terracotta, corten, marble, shingle and standing seam. Window styles are in `windowMask`: none, grid, ribbon, curtain wall, arched, porthole, slit and loft.

## 8. Laws of the simulation

The simulation brings the city to life. It reads the plan and never changes a building.

**Clock.** One hour is 30 seconds at normal speed, so a day takes twelve minutes. The sun, sky, fog, window lights, street lamps and traffic all follow one clock in `sky.js`.

**Windows.** Offices light up from morning to evening. Homes light up after dusk. A few rooms stay lit all night, and some rooms switch on and off as time passes.

**Traffic.** Cars drive on the right, keep their lane, follow the car ahead, stop at signals and curve through junctions. Signals run on a 22 second cycle with an offset at each junction. Demand follows the hour, peaking at 8:00 and 17:00.

**Citizens.** Each has a home, a workplace and a favorite spot among nearby lots, chosen by what those lots are zoned for. They walk the sidewalks on a daily schedule: work, sometimes lunch, then home or an evening out. Surnames come from the `word` of their home, so the crowd belongs to the repository.

**Reported numbers.** Residents are lines of code divided by 8, or three per building if that is greater. Up to 1,500 citizens are simulated individually, and "At work" is scaled up from them. "Walking" and "Cars" are exact counts.

**Events.** Each event is drawn from something true about the repository, in `sim/events.js`.

| Event | Source |
|---|---|
| Fire and fire crew | A file with FIXME or HACK markers |
| Crews at work | A file with TODOs |
| Census | Language shares |
| Exam week | A test file |
| Output report | A factory, power station or silo |
| Library holdings | A long document |
| Fireworks | Some evenings, over City Hall |
| Weather | Random, chosen on the sim clock |

**Above and around.** The Main Line train circles the city. Planes, a helicopter and a blimp bearing the repository's name fly overhead, and ships cross the harbor to the south.

## 9. Tours and lenses

Tours live in `public/js/camera/tours.js`. A **flight** is a list of waypoints flown on a spline, with captions built from real numbers. A **ride** attaches the camera to something that moves.

Flights keep clear of buildings automatically, using the city's height grid. Mark a waypoint `street: true` to fly below rooftop level along a road.

Lenses recolor buildings by data without touching geometry. They are defined in the `LENSES` list in `public/js/main.js`.

## 10. Laws of play

The city is also a playground. Every rule below lives in `shared/game/`, which runs unchanged on the server, in the browser and in the tests.

### Who decides what

The room (`shared/game/room.js`) is the referee. It holds who is it, who took which pad, which gate each racer passed, and who holds which item. It never sees a map: every client derives the same sites and courses from the same city, so a pad number or a gate number means the same place for everyone. Players report their own position, and the room judges tags from those positions, so a tag cannot be claimed from across town.

```
Browser                                       Server
  motion.js moves your body        ->  p      room.js stores it
  collision.js stops you at walls             tag, trap and timer checks, 15 times a second
  layout.js places pads and gates  <-  s      every body, 15 times a second
  interpolates other players 120 ms behind    game, tagged, gate, fx events
```

The offline preview runs the same `Room` class inside the page, so solo play with bots has no separate code path.

### Rooms

- Each repository has a public room. A full room opens another one. A private room is reached only with its six-character join code, and the code goes when the last person leaves.
- Rooms live in one server process and nothing is written down: no accounts, no history, no saved scores. Track records and review stamps last as long as the room.
- Screen names and looks are kept only in the player's own browser.
- Traffic is local to each player. It pushes back, but it is scenery, not shared state.

### Bodies and vehicles

<!-- table: vehicles -->
| Vehicle | Top speed | Battery | Character |
|---|---|---|---|
| On foot | 9.5 m/s, 14 sprinting | None | Run |
| Scooter | 23 m/s | 70 s | Quick through traffic |
| Electric car | 36 m/s | 95 s | Fastest on the road |
| Drone | 25 m/s | 45 s | Flies over everything |
<!-- /table -->

Buildings are solid below their roof line and hold you up above it, so a drone can land on a tower and a player can walk off the edge. A vehicle battery only runs down while it is in use, and a race switches batteries off. Riders are put back on foot when it runs out.

### Items

A pad gives a random item, and a player holds one at a time.

<!-- table: items -->
| Item | Effect | Lasts |
|---|---|---|
| Hotfix | A burst of speed | 5 s |
| Force push | Shoves everyone near you away | Instant |
| Merge conflict | Drops a trap that stops the next player | Instant |
| Stash | Hides you from the map and fades you out | 7 s |
| Protected branch | Nobody can tag you for a while | 6 s |
| Segfault | Kills the power to nearby vehicles | Instant |
| Rebase | Teleports you across town | Instant |
<!-- /table -->

### Game modes

<!-- table: modes -->
| Mode | Goal | Fewest players |
|---|---|---|
| Tag | One player is it. Spend the least time being it to win. | 2 |
| Infection | Everyone tagged joins the hunt. Survive to the buzzer. | 2 |
| Race | Pass every gate in order, then beat everyone to the line. | 1 |
<!-- /table -->

- **Tag** plays inside a ring around one junction, sized to the crowd, so players meet. The first player chosen is held for 3 seconds. A new It waits 2.5 seconds before tagging, and the player who tagged them is immune for 3.
- **Race** courses are found on the real road graph (`buildCourses` in `layout.js`), so each repository gets its own tracks. A recipe gives waypoints; a route search joins them, never turning straight back and avoiding roads already used. Gates sit on every corner and at even spacing on the straights, and only the next gate in order counts. A drone race moves the gates into the air.
- **Bots** are ordinary room players whose movement runs in the browser of the person who started the game, using the same motion code as a human. They leave when that person does.

### Modes and cameras

There are four modes: Explore, Walk, Review and Tour. `world.currentMode` works out which one you are in from what is actually happening, so the bar can never disagree with the screen, and `world.setMode` is the only way between them. Review opens over Walk without ending it.

Nothing cuts. Code that is about to change what drives the camera calls `world.glide()`, which records the current view, and the camera then eases from that view to wherever the new mode puts it. First and third person are one camera at different distances from the head, so the wheel can travel between them.

A view link records the camera's position, rotation and lens, the hour, the weather, the data lens and the selected building. Opening one holds the camera still until the viewer moves it. It does not carry a private room's code.

### Keyboard only

A setting in More, and the K key. The mouse stops steering, so a click only ever means a click: arrow keys turn the camera and W A S D move. Every part of this reads `prefs.keyboardOnly`, and no pointer lock is ever taken while it is on.

### Going indoors

Every building can be entered. The inside is a separate scene that `shared/interior.js` plans from the file and `public/js/world/interior.js` draws.

- Each line of the file becomes one object, standing along rows in reading order. Only a file too long for 60 floors of 240 lines is sampled, evenly.
- Floors follow the building: the same count as outside, more if the file needs them. Every floor is a stretch of the file, and the directory by the lift says which.
- Buildings with more than one floor have a lift and a staircase in the same place on every floor. The stairs are real slopes under your feet, in two flights with a landing.
- Function and type names stand on plates over their objects. Posters show real code, a board lists open TODOs, and the television plays the floor back as scrolling code, line lengths, nesting rings, common names or a map of what each line does.
- The room is furnished for the building's use, and its palette comes from there too.
- If the text cannot be fetched, the rooms are laid out from the file's measurements and the building says so.
- A round is played outdoors, so joining one brings you back out onto the street.
- The room passes on which building you are in, so people in the same building see each other and nobody else does.

Line kinds:

| Kind | Object | Height comes from |
|---|---|---|
| Type | Tall six-sided pillar, purple | Line length |
| Function | Upright slab, blue, with a nameplate | Line length |
| Loop | Ring, teal | Fixed |
| Branch | Forked post, gold | Fixed |
| Return | Green point | Line length |
| Failure | Red diamond | Fixed |
| Assignment | Pale blue jar | Line length |
| Import | Wooden crate | Line length |
| Comment | Stack of books | Fixed |
| TODO | Orange cone | Line length |
| Closing bracket | Low block | Fixed |
| Anything else | Grey box | Line length |

A blank line leaves a gap. Nesting moves an object back along the row and lifts it a little. `classifyLine` in `shared/codeline.js` decides the kind. To add one, teach it the new kind, add it to `LINE_KINDS`, and give it a shape in `SHAPES` and a color in `KIND_COLOR`.

### Music

`shared/music.js` writes a piece of music from one file, and the same file always gives the same piece.

| Part of the file | Part of the music |
|---|---|
| Hash | Key, voices, chord progression, drum pattern |
| Comments, branches, TODOs | Mood: major for well-explained code, minor for code with debt |
| Branches per 1,000 lines | Tempo, and finer hi-hats |
| Functions per 1,000 lines | A walking bass |
| Indentation of a line | Pitch of its note |
| Length of a line | Length of its note |
| Blank line | A rest |
| Kind of line | How hard the note is played |

A piece runs through six sections and adds its layers one at a time: pad, bass, tune, arpeggio, keys and drums. When it ends, the next part follows on from further through the same file. Strong beats settle on a note of the current chord and big leaps fold back, so the code stays singable.

Anyone can choose the file from the sound panel or a building's card, and the building you walk into can set it. Music, effects and city sounds each have their own volume. The music softens indoors and the drums come forward during a round.

### Time machine

Press Y, or use More, to replay the city's last 400 commits. Dragging the timeline moves the city to any commit, and it can play through them. A building appears when its file was added and rises with each change until it reaches the height it has today, and files in the commit are marked, green for added and amber for changed. Hills raised by activity swell as the changes build up.

It shows what the history can show. The history is fetched without file contents, so a building's height at a commit is an estimate from how much of its change was still to come, and not the file's true size then. Anything older than the 400 commits stands from the start. Files since deleted are not in the city, so they are counted and not drawn. The time machine is yours alone and only works in Explore.

### Dependency traffic

Press U, or use More, to see which files use which. Links are arcs between rooftops, and pulses run from the file that imports to the file it needs. With nothing selected the busiest links show. Selecting a building shows its own: blue for what it uses and amber for what uses it. The inspector lists both, and each entry goes to that file.

`server/imports.js` reads imports in JavaScript, TypeScript, CSS, Python, Go, Rust, Java, Kotlin, Scala, C, C++, Objective-C, Ruby and PHP, then looks each name up among the repository's own files. Outside libraries are dropped. It uses plain patterns, so it can miss a clever import, but it never invents a link. Scripts can read `file.uses` and `file.dependents`, buildings can be chosen with `building[dependents>=10]`, and terrain can use `source: dependents`, which puts the files everything relies on at the top of the hills.

### Finding things

`/` or Ctrl F opens one box from any mode. It searches files, names defined in code, streets and avenues, players, landmarks and, indoors, the lines of the current file. Enter flies there from above. While walking, Enter points the way with a compass and a column of light, and Shift and Enter takes you there, straight to the right floor for a line of code. Only names a file defines are listed, not every place they are used.

### Controllers

The phone is drawn as a gamepad with labels that change with what you are doing, and everything the keyboard can reach has a button: reviews, holograms, floors, the flashlight and the mode bar. A real gamepad has the same map. `controller()` in `game.js` handles anything that is not running around the street, including the two questions a dialog can ask.

### Race courses

Besides the shaped circuits, each city gets **tree walks**: a lap through one of its busiest directories. A directory's streets are spread out by first letter, so a lap starts at the directory's largest file and hops to the nearest unvisited one, favoring folders it has not seen yet, until it has used its distance budget. Each file on the route gets a named gate and a beacon, and passing it counts as a visit. The **Landmark Run** does the same with the largest files in the repository.

### Review

There are two ways to review, and each person picks one. The **desk** is a panel beside the city. A **hologram** is a panel standing in the street, which everyone in the room sees.

- A file hologram is a scratch copy. People type in it together, each with their own colored caret, and edits are never written to GitHub. The room orders the edits and rewrites crossing ones (`shared/game/textops.js`), so everybody ends up with the same text. It holds positions only, never the file.
- A commit or pull request hologram is read only. Selecting a line and pressing 1 to 4 stamps it, and a file row opens that file as its own hologram.
- Scrolling is shared unless a person switches to scrolling on their own.
- A room holds up to 8 holograms, and opening another closes the oldest. Each one takes up to 4,000 edits.
- A panel reads correctly from both faces, and anyone can pick one up and set it down elsewhere. The room keeps its position, so it moves for everyone.

The review desk reads through `server/review.js`, which turns each request into one fixed, read-only GitHub path and trims the answer to the fields the desk shows. Opening a commit or pull request lights its files in the city: added green, modified amber, renamed blue, unchanged buildings dimmed. Files a change touched but the city does not hold are counted and named as missing. Stamps are the room's shared marks on lines. There is no free text between players anywhere, only stamps, quick chat and emotes.

## 11. City scripts

A city script is a small text file that changes how a city is built and looks, without changing how it plays. It reads like a stylesheet: choose a parent, then override what you want. Everything not mentioned stays as the parent has it.

```
@city "Harbor Noir";
@extends neon;
@layout metropolis;

height { scale: 1.4; }
look { start-hour: 20; fog: 2; }
language[Python] { wall: #336699; low: brick; }

javascript {
  function pressure(file) { return file.branches + file.todos * 4; }
}

building[language="Python"][path^="src/"], building[tier>=4] {
  material: brick;
  height: js(file.lines * 0.2 + pressure(file));
  add: flag;
}

rule[O-19] { enabled: false; }
```

Open **More, then City script** to write one. Scripts are at most 16 KB, are saved as `.city` files, and a share link carries the whole script after the `#`, so it never reaches the server's logs.

### What scripts may change

Scripts change the plan and the presentation: which building a file becomes, its size and color, the street arrangement, the land, the light and the weather. The rules of play do not change. Movement, tag, racing, items, review and rooms are the same code, because they read the plan like any other city, and collision, courses and pads are derived from it.

The whole room must share one plan, so a script belongs to its room. A public room only holds players with the same script. In a private room the host chooses, and everyone moves together. The server keeps a script's text and passes it on, and it never runs one.

### Charters and themes

A charter is the law of a place: its streets, its land, how tall it builds. A theme is how it looks. Any charter takes any theme, and leaving one out keeps the default, which is the rulebook in this document.

```
@charter hilltowns;
@theme frost;
terrain { height: 120; }
```

<!-- table: scriptCharters -->
| Charter | Character |
|---|---|
| `default` | The rulebook in this document |
| `boroughs` | Every root folder is a town of its own, joined by highways |
| `archipelago` | Every root folder is an island, and bridges carry the road |
| `hilltowns` | The busiest files hold the hilltops, and streets wind up to them |
| `radiant` | Ring roads and spokes rise toward a great central circle |
| `garden` | Quiet cul-de-sacs, low homes and a tree on every street |
| `nautilus` | One great street spirals up to a summit at the center |
| `amphitheater` | Stepped crescents of streets look down on the bay |
| `piazza` | Crooked lanes open onto a piazza at every turn |
| `arcology` | A megacity on great plateaus, with towers past the clouds |
| `frontier` | Lonely farms and ranches along a mountain range |
<!-- /table -->

<!-- table: scriptThemes -->
| Theme | Character |
|---|---|
| `default` | Brick, stone and glass, by language |
| `brutalist` | Raw concrete, flat roofs and few windows |
| `neon` | Dark towers, bright signs and a city that stays up late |
| `storybook` | Pastel houses with steep roofs |
| `solarpunk` | White terraces, roof gardens and golden morning light |
| `dune` | Sandstone towers under a long amber dusk |
| `frost` | Deep snow, blue hour and warm lit windows |
| `vaporwave` | A pink and teal sunset that never ends |
| `noir` | Black and white, rain and a single lit window |
| `ember` | Black basalt and rivers of light under an ash sky |
| `blueprint` | The city as its own drawing, in white on architect's blue |
<!-- /table -->

Both are ordinary scripts in `shared/script/parents.js`, and they are the best examples of the language. A charter runs first, then a theme, then `@extends` if there is one, then the script itself, so the last word is always yours. The City setup dialog writes these same `@` lines, so a choice made there can be shared or edited like anything typed by hand.

### Blocks

<!-- table: scriptBlocks -->
| Block | What it sets |
|---|---|
| `height` | The height law, for every file. Buildings still rise with file size unless a building block says otherwise. |
| `tiers` | How many floors make each height tier. Tiers choose forms and roofs. |
| `layout` | The ground plan. These change where things stand, and every player in a room shares them. |
| `terrain` | Hills. Elevation is only drawn, so it never changes a race or a game of tag, and any player can turn it off. |
| `streets` | Junctions. A share of junctions opens into a loop of road, wherever the buildings leave room. |
| `district` | One root folder in a boroughs or archipelago layout. Written district["src"] { layout: metropolis; }. |
| `look` | Light, color and atmosphere. These change nothing about where things are. |
| `language` | One language's neighbourhood. Written language[Python] { ... }. |
| `arch` | One building type. Written arch[office] { ... }. |
| `forms` | Which building types source code may become at a height. Written forms[3] { allow: office, slab; }. |
| `rule` | One rule from the charter. Written rule[O-19] { enabled: false; }. |
| `building` | Any matching building. Later blocks win. Every value here can be js(). |
| `javascript` | Functions and values that js() can use |
<!-- /table -->

A script's blocks run in order, and a later value replaces an earlier one. A `building` block overrides everything else, because it runs last. `arch` and `use` run right after zoning, so the rules that follow see them, and everything else runs after the rulebook has finished. The inspector lists each block that changed a building as `X-` and its line.

### Choosing buildings

Each `[test]` must pass, and a comma means or. `building[tier>=3][!test], building[name$=".md"]` is a fair example. Text compares with `=`, `!=`, `^=` (starts with), `$=` (ends with) and `*=` (contains). Numbers compare with `=`, `!=`, `>`, `>=`, `<` and `<=`. Tests like `code` stand alone, and `!` negates them.

<!-- table: scriptSelectors -->
| Test | Meaning |
|---|---|
| `language` | Language name |
| `path` | Full path |
| `name` | File name |
| `folder` | Folder path |
| `arch` | Building type |
| `use` | Zoning |
| `tier` | Height tier, 1 to 5 |
| `floors` | Floors |
| `depth` | Folders deep |
| `size` | Bytes |
| `lines` | Lines of text |
| `comments` | Share of lines that are comments |
| `todos` | TODO count |
| `changes` | Times this file changed in recent history |
| `uses` | Other files this one imports |
| `dependents` | Other files that import this one |
| `code` | Source code |
| `test` | A test file |
| `binary` | A binary file |
<!-- /table -->

### Every parameter

<!-- table: scriptProps -->
**height**

| Property | Accepts | Sets |
|---|---|---|
| `floor-height` | 2 to 8 | Meters in one floor |
| `curve` | 0.02 to 2 | How fast floors climb with file size |
| `base-size` | 16 to 65536 | File size in bytes where a second floor begins |
| `scale` | 0.05 to 8 | Multiplies every floor above the first |
| `lift` | 0 to 60 | Floors added to every building |
| `width-scale` | 0.4 to 2.5 | Multiplies every footprint |

**tiers**

| Property | Accepts | Sets |
|---|---|---|
| `tier-2` | 1 to 200 | Floors where tier 2 begins |
| `tier-3` | 1 to 200 | Floors where tier 3 begins |
| `tier-4` | 1 to 200 | Floors where tier 4 begins |
| `tier-5` | 1 to 200 | Floors where tier 5 begins |

**layout**

| Property | Accepts | Sets |
|---|---|---|
| `land` | Color | Color of open ground |
| `traffic` | 0 to 3 | Cars, as a share of the layout's usual number |
| `people` | 0 to 3 | People, as a share of the layout's usual number |
| `sidewalks` | true or false | Whether streets have sidewalks |
| `lamp-spacing` | 16 to 300 | Meters between street lamps |
| `space` | 0.8 to 3 | How far apart a curved layout spreads things |
| `keep-roads` | 0.05 to 1 | Share of spare roads a curved layout keeps |
| `curve` | 0 to 2 | Multiplies how much a curved layout bends |
| `setback` | 0.8 to 2 | How far small buildings stand back in a curved layout |
| `town-gap` | 60 to 600 | Meters between towns in a boroughs or archipelago layout |

**terrain**

| Property | Accepts | Sets |
|---|---|---|
| `enabled` | true or false | Whether this city opens with hills |
| `source` | One of the names | What raises the ground. activity is how often a file changes. script reads elevation from building blocks |
| `shape` | hills, terraces, mesas, ridges, basin, peaks, dome | The character of the land |
| `height` | 4 to 160 | Height of the tallest hill in meters |
| `reach` | 24 to 400 | How wide a hill spreads, in meters |
| `noise` | 0 to 1 | Share of the land that is natural rise and fall |
| `steps` | 2 to 12 | Levels in terraces |
| `roadworks` | true or false | Whether roads are graded, with cuttings, bridges and retaining walls |
| `grade` | 0.02 to 0.3 | The steepest a road may climb, as rise over distance |

**streets**

| Property | Accepts | Sets |
|---|---|---|
| `roundabouts` | 0 to 1 | Share of junctions that become roundabouts |
| `plazas` | 0 to 1 | Share of junctions that become plazas with a fountain |

**district**

| Property | Accepts | Sets |
|---|---|---|
| `layout` | grid, metropolis, suburbs, radial, spiral, crescent, zigzag, countryside | The layout of this town |

**look**

| Property | Accepts | Sets |
|---|---|---|
| `exposure` | 0.2 to 2.5 | Overall brightness |
| `glow` | 0 to 2 | Strength of the bloom around bright lights |
| `fog` | 0 to 8 | Multiplies the fog |
| `sky-tint` | Color | Mixed into the sky |
| `sky-mix` | 0 to 1 | How strongly sky-tint is mixed in |
| `sun-tint` | Color | Color of the sunlight |
| `window-glow` | Color | Color of lit windows at night |
| `start-hour` | 0 to 23.99 | Hour the city opens at |
| `clock-speed` | 0 to 16 | Multiplies how fast the clock runs. 0 holds the hour |
| `weather` | auto, clear, cloudy, rain, fog, snow | Fixes the weather, or auto to let it change |
| `road` | Color | Road surface |
| `road-line` | Color | White lines on the road |
| `road-stripe` | Color | Center stripe on the road |
| `pavement` | Color | Sidewalks, plazas and lots |
| `grass` | Color | Lawns and parks |
| `foliage` | Color | Trees |
| `water` | Color | Deep water |
| `shore` | Color | The beach around the land |
| `far-hills` | Color | Hills on the horizon |
| `rock` | Color | Steep ground |
| `ambient` | 0 to 4 | Multiplies the light that fills shadows, which lets a night city be read |

**language**

| Property | Accepts | Sets |
|---|---|---|
| `wall` | Color | Wall color |
| `trim` | Color | Trim color |
| `roof` | Color | Roof color |
| `map` | Color | Color on the map and in the language lens |
| `low` | One of the names | Wall material up to tier 3 |
| `high` | One of the names | Wall material from tier 4 up |

**arch**

| Property | Accepts | Sets |
|---|---|---|
| `width` | 4 to 60 | Footprint width in meters, before scaling |
| `depth` | 4 to 60 | Footprint depth in meters, before scaling |
| `name` | Text, up to 60 | Word used in building names |

**forms**

| Property | Accepts | Sets |
|---|---|---|
| `allow` | Names, separated by commas | Building types allowed at this tier |

**rule**

| Property | Accepts | Sets |
|---|---|---|
| `enabled` | true or false | Whether this rule runs. js() can decide per building |

**building**

| Property | Accepts | Sets |
|---|---|---|
| `arch` | One of the names | Building type |
| `use` | home, work, shop, civic, industry | Zoning |
| `height` | 2 to 700 | Height in meters |
| `floors` | 1 to 200 | Floors |
| `width` | 4 to 60 | Footprint width in meters |
| `depth` | 4 to 60 | Footprint depth in meters |
| `material` | One of the names | Wall material |
| `wall` | Color | Wall color |
| `trim` | Color | Trim color |
| `roof-color` | Color | Roof color |
| `roof` | parapet, mechanical, spire, crown, gable, hip, mansard | Roof shape |
| `windows` | none, grid, ribbon, curtain, arched, porthole, slit, loft | Window style |
| `bay` | 1.6 to 8 | Width of a window bay |
| `occupancy` | 0 to 1 | Share of rooms lit at night |
| `grime` | 0 to 1 | How weathered the walls look |
| `ground-shop` | true or false | Whether the ground floor is a storefront |
| `name` | Text, up to 60 | The building's name |
| `elevation` | 0 to 1 | How high this lot stands, for terrain with source: script |
| `add` | Names, separated by commas | Ornaments to add |
| `remove` | Names, separated by commas | Ornaments to take away |
<!-- /table -->

### JavaScript

In a `building` block any value can be `js(expression)`, and so can `enabled` in a `rule` block. A `javascript { }` block holds functions and values they share. A result outside a property's range is pulled back into it.

<!-- table: scriptNames -->
| Name | Meaning |
|---|---|
| `file` | This file: path, name, folder, language, size, lines, changes, uses, dependents, depth, tier, floors, code, test, binary, comments, todos, fixmes, functions, classes, imports, branches, sha |
| `building` | The design so far: arch, use, height, floors, width, depth, material, wall, trim, roof, windows and the rest |
| `repo` | The whole city: name, files, lines, languages, largest (bytes in the biggest file) |
| `value` | The value this property has before this block |
| `random` | A seeded number from 0 to 1, different for each building and the same for every player |
| `clamp` | clamp(value, low, high) |
| `lerp` | lerp(a, b, t) |
| `mix` | mix(colorA, colorB, t) blends two colors |
| `hsl` | hsl(hue, saturation, lightness) gives a color. Hue is 0 to 360, the others 0 to 1 |
| `hash` | hash(text) gives a stable number from 0 to 1 for any text |
<!-- /table -->

JavaScript here runs in a small interpreter of its own (`shared/script/sandbox.js`), not in the browser's engine, because a link can carry a script from a stranger. That has three consequences:

- It reaches nothing. There is no page, network, storage or timer, and objects from the city are frozen. Nothing leads back to the host: `constructor`, `__proto__` and `prototype` do not exist.
- It stops. Each call has a step budget, and so does each city. A value that runs out or throws keeps its parent value, and the problem is reported with its line.
- It repeats. `random()` and `Math.random()` are seeded from the file, so every player gets the same city. Without that the buildings would differ between players and collisions would disagree.

The language is modern JavaScript without classes, `new`, `this`, `async`, generators, regular expressions, getters or labels.

### The analyzer

`analyzeScript` in `shared/script/analyzer.js` checks a script before anything is built, and a script with errors does not load. The editor runs it as you type, `npm run check-city -- my.city examples/flask.json` runs it from a terminal, and the app runs it again on any script that arrives by link or from a room.

- **Errors** stop a script: syntax, an unknown block, property, parent, layout, rule, language or building test, a value of the wrong type or out of range, JavaScript that does not parse, uses something unavailable or names something undefined.
- **Warnings** do not: a property set twice, a block no building matches, a layout setting with no effect here, and anything a trial run on the open city finds, such as a `js()` that throws or gives the wrong type.

### Changing a city while it stands

Light, weather, the sea and hills change at once, even while a script is being typed: `shared/script/share.js` gives each script a structure key, and two scripts with the same key build the same city. Anything else moves or repaints what was built, so the page rebuilds and the camera comes back where it was. In a private room the host's change reaches everyone the same way.

### Add a script parameter

Add it to `SCHEMA` in `shared/script/schema.js`, and the analyzer, the editor's reference and the table above all follow after `npm run charter`. Then apply it: a `building` property goes in `SPEC_FIELD` in the analyzer, a `layout` property in `shapeCity`, and a `look` property in `createWorld`.

## 12. Adding to the charter

### Add a rule

Add one object to the right list in `shared/rulebook/index.js`, then run `npm run charter` and `npm test`.

```js
// In ORNAMENT: a file that mentions a database gets a satellite dish.
{
  id: 'O-20',
  title: 'Database clients get a satellite dish',
  when: (f, s) => f.tier >= 3 && /sql|postgres|mongo/.test(f.path),
  then: add('antenna', { count: 1 }),
},
```

`when(f, s)` receives the file view and the specification so far. `then(s, f, rng)` changes the specification. Only use the `rng` passed in, so the city stays deterministic.

Order matters in **first** stages. Put a new rule above the rule it should take priority over.

### Add a measurement

1. Measure it in `analyze` in `server/dna.js`.
2. Add it to the table in part 3.
3. Add a scaled value to `m` in `viewFile` if rules will compare it.
4. Regenerate the example cities with `npm run examples -- <folder of checkouts>`.

Cities cached before the change will not have the new field, so read it with a fallback: `f.d.newField ?? 0`.

### Add an archetype

1. Write a builder in `public/js/world/archetypes.js` and add it to `BUILDERS`.
2. Add its footprint to `FOOTPRINT` and its name to `BUSINESS` in the rulebook.
3. Point a rule or a form table at it.

A builder has three duties. The body must reach `s.h`, because height is law. It should call `setDeck` so that rooftop ornament has somewhere to stand. It should be built facing +z, the street. `test/rulebook.test.js` checks each builder at three sizes.

### Add an ornament

Add a function to `ORNAMENTS` in `archetypes.js`, then add a rule that pushes `{ kind: 'yourname' }`.

Geometry that moves or glows cannot live in the merged city mesh. Ask the collector for it instead, as cranes and smoke do: add a method in `world/fx-collector.js`, and draw what it collects in `world/fx.js`.

### Add a language

Add one line to `LANGUAGE_STYLE` in `shared/rulebook/languages.js`, and map its file extensions in `server/languages.js`.

### Add a wall material or window style

1. Add the id to `MAT` or `WIN` in `shared/constants.js`.
2. Add a branch to `wallPattern` or `windowMask` in `facade.js`. A window style returns its mask and its average coverage, which is used once the windows are smaller than a pixel.
3. Reference it from a rule.

### Add a tour

Add an entry to the object in `createTours`, with `name`, `blurb` and either `flight()` or `ride`. It appears in the menu and in autoplay automatically. Set `available: false` when the repository lacks what the tour needs.

### Add an example city

Choose a small repository under a permissive license and add it to `shared/examples.js`, with its license file's name. Run `npm run examples -- <folder of checkouts>` and `npm run notices`, then link it from the landing page. Never add a repository whose license does not allow copying.

### Add a picture spot

Add a placement function in `public/js/world/art.js` that calls `panels.upright` or `panels.add`. Use `lit` for anything backlit.

### Add an event

Add an option in `CityEvents.pick` in `sim/events.js`. Hold to the rule that every event reports something true about the repository, and that important ones are visible in the world as well as in the feed.

### Add a lens

Add an entry to `LENSES` in `main.js`. `value(lot)` returns a linear RGB color.

### Rules for rule writers

- Rule ids are permanent. Retire a rule by deleting it, and never reuse its id.
- Tests fail if a rule, archetype or language is missing from this charter, so run `npm run charter` before committing.
- Raise `CHARTER_VERSION` in the rulebook when a change alters how existing repositories look.
- Keep rules about one thing. Two small rules are easier to read in the file card than one large one.
- A rule should have a reason that a person could guess. "Comments pay for solar panels" works because comments are a public good. If a mapping feels arbitrary, it probably needs a better idea.

### Adding to the game

Tables that describe the game are generated from `shared/game/protocol.js`, so run `npm run charter` after changing it.

**A new item.** Add an entry to `ITEMS` and its id to the end of `ITEM_IDS`. Server-side effects go in `Room.useItem`, and what a player feels goes in `Game.onEffect`.

**A new vehicle.** Add it to `VEHICLES` and to the end of `VEHICLE_IDS`, since these numbers travel in every update. Give it a step function in `motion.js` if it does not move like an existing one, and a model in `models.js`.

**A new game mode.** Add it to `MODES`, then give it a branch in `Room.start`, `Room.tick` and `Room.standings`, and its HUD in `Game.updateRoundHud`.

**A new course shape.** Add a recipe to `buildCourses`. Any list of waypoints works, because the route search does the driving. Pass `visits` to give listed lots their own named gates.

**A new city style.** Add an entry to `CITY_STYLES` in `shared/shape.js`, with its spacing, bends, share of roads kept and height scale, and say what empty ground becomes in `PARK_FATE`. Keep each bend's size well under its length times the spacing, so the land cannot fold.

**A new stamp, quick chat line or avatar option.** Add it to the end of its list in `protocol.js`. These are sent as indexes, so the order of existing entries must stay the same.

## 13. Ideas for future versions

- **History.** Pull commit data so that age weathers a building and churn draws crowds. The tarball has no history, so this needs the GitHub API and a token.
- **Skyline scrubber.** Replay releases and watch the city grow.
- **Bridges.** Draw imports between files as bridges or transit lines.
- **Districts.** Give top-level folders their own boroughs, separated by rivers.
- **Elevation.** Raise a street's ground with directory depth, so deep folders stand on hills.
- **Neighbor rules.** Use the context stage more, for example towers casting a civic plaza at their base.
- **Compare mode.** Show two branches side by side, with changed buildings lit.
