Skip to content

README accuracy pass, and fix the capture that framed an empty city - #145

Merged
thalida merged 5 commits into
mainfrom
docs/readme-accuracy
Jul 26, 2026
Merged

README accuracy pass, and fix the capture that framed an empty city#145
thalida merged 5 commits into
mainfrom
docs/readme-accuracy

Conversation

@thalida

@thalida thalida commented Jul 26, 2026

Copy link
Copy Markdown
Owner

An audit of the README against main after the last five PRs, plus the two bugs that fell out of trying to regenerate its assets.

README

Three claims had gone stale:

  • Deleted files in Timeline described the rendering ("leave a faint gray stub"), which was wrong twice over: the stub height default went from 2 floors to 0.05, and they're crossed out now. Rewritten to describe what it gets you rather than what it looks like, so it stops going stale each time the treatment is retuned.
  • The wire-types bullet claimed the OpenAPI schema was "drift-guarded by manifest.contract.ts". Deleted: the commands table already documents just gen-types, and the mechanism it named is mostly gone since Move three derived values to the backend, and stop re-declaring the wire types #142 — those types are derived from the generated schema now, so field drift can't happen rather than being caught. What survives only pins the NodeKind discriminator.
  • The roof border was missing from the building signals. Saturation and lightness were documented as the aging axes with nothing saying what they fade from.

Screenshots regenerated against current main, so they show the crossed-out deleted files and the roof-border reference colour. The timeline alt text moved with the image it describes.

Verified mechanically rather than by eye: all 12 just recipes the commands table promises exist, and the documented skip list is an exact 44/44 match with ALWAYS_SKIP.

The pre-push gate depended on the host

Steps 1–3 ran in containers; 4–6 shelled out to cd app && npm run …. So the gate quietly required app/node_modules, and a fresh worktree couldn't push at alleslint: command not found, for a change that touched one markdown file. They're one containerized step now, running the same command just lint already used, so the gate and the recipe can't drift.

just demo-video recorded an empty city

The demo orbit was recording a close-up of a single road. Two defects behind it:

RebuildStatus.Idle was both the initial value and the "finished" value. Anything waiting for Idle passed at boot, before markRebuilding() had run. There's a Pending state now, so "nothing has been built yet" and "a build finished" are answerable apart.

Idle isn't the right question anyway. The skeleton apply reaches it too, via the trees decoration pass, with a city of streets and no buildings. Instrumenting the shot gave:

cityRadius: 78.9   distance: 74.9   tallest: 0   rootStreetWidth: 32

tallest: 0 — no buildings. The bbox was the root street alone: radius 79 world units, where a ten-floor building is already 160 tall. The camera locked onto that and orbited it for the full 18 seconds, because orbit never returns false and so never retried, unlike the trees shot.

The harness now waits for a tallest building before posing. Reading anchors in the gate subscribes to bbox, so it re-fires once buildings land, and it covers every shot rather than the one that happened to expose it.

Two wrong turns kept in the history

The first attempt replaced cityRadius * 0.95 with proper FOV-and-aspect fitting math. That was wrong: the committed video, recorded with that exact multiplier, frames correctly — reverted. The second guarded inside the orbit shot, fixing one call site while every other shot kept the trap; superseded by the harness gate.

Both are on the branch rather than amended away, since the squash-merge cleans it up.

Verified by re-recording and comparing extracted frames against the previously committed video, not by reading the code — which is how the first wrong turn got caught.

Full gate green via the pre-push hook (now containerized end to end): 369 pytest, ruff, 2838 vitest, eslint, prettier, typecheck.

🤖 Generated with Claude Code

thalida added 5 commits July 26, 2026 12:53
Deleted files: described the rendering ("faint gray stub"), which had
gone stale twice over — the stub height default dropped from 2 floors to
0.05 and they're crossed out now. Describes what it gets you instead, so
it stops going stale every time the treatment is retuned.

Wire types: dropped the backend bullet about the OpenAPI schema being
"drift-guarded by manifest.contract.ts". The commands table already says
`just gen-types` regenerates the wire types from the schema, and the
mechanism it named is mostly gone: those types are derived from the
generated schema now, so field drift can't happen rather than being
caught. What's left only pins the NodeKind discriminator.

Roof border: added to the building signals. Saturation and lightness were
documented as the aging axes with nothing saying what they fade FROM.
Steps 1-3 ran in containers; 4-6 shelled out to `cd app && npm run ...`
on the host. So the gate quietly depended on app/node_modules existing,
and a fresh worktree couldn't push at all — eslint: command not found,
for a change that touched one markdown file.

They're one containerized step now, running the same command `just lint`
already used, so the gate and the recipe can't drift. Slower by one
container start; in exchange the gate no longer cares what the host has
installed, which is the point of having it.
Regenerated against current main, so the shots show this session's work:
crossed-out deleted files in Timeline, and the roof-border reference
colour on the buildings.

The timeline alt text described the old treatment ("gray stubs"), so it
moves with the image it describes.

demo.mp4 is deliberately not in here — it re-recorded too zoomed in.
The orbit posed against a city that had streets but no buildings yet, so
the bounding box was just the root street: radius 79 world units, when a
ten-floor building is already 160 tall. The camera locked onto that and
orbited a close-up of one road for the full 18 seconds.

The harness gate (RebuildStatus.Idle) goes true while the city is still
assembling, and orbit never returned false, so unlike the trees shot it
never retried — it committed to the first pose it got. It now defers
until a tallest building exists, which is the cheapest available proof
that buildings have landed. The retry budget is 48s, so there's room.

Re-recorded demo.mp4 against this.
Correcting the previous commit: guarding inside the orbit shot fixed the
symptom in one place while every other shot kept the same trap.

Two things were wrong. REBUILD_STATUS started at Idle, so "nothing has
been built yet" and "a build finished" were the same value and the
harness's wait passed at boot; that's now Pending. And Idle isn't the
right question anyway, since the skeleton apply reaches it too, with a
city of streets and no buildings — a bbox of the root street alone,
radius 79 where a ten-floor building is 160 tall.

The harness now waits for a tallest building to exist. Reading anchors in
the gate subscribes to bbox, so it re-fires when the buildings land, and
the fix covers every shot rather than the one that happened to show it.

Verified by re-recording: the orbit frames the whole city again.
@thalida
thalida merged commit 6beb1a4 into main Jul 26, 2026
1 check passed
@thalida
thalida deleted the docs/readme-accuracy branch July 26, 2026 17:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant