Works03 / 05
An unofficial GTA 6 reference where a fact is a record, not a sentence: four statuses, a source trail, and a status that cannot change without a dated reason.
thecity.gg

- Product
- Unofficial GTA 6 reference; not affiliated with Rockstar Games or Take-Two
- Role
- Engineering: content model, site, admin panel, hosting
- Period
- September – October 2026
- Stack
- Next.js 16, React 19, TypeScript, PostgreSQL 17, Claude API, Docker
- Live
- thecity.gg
Most GTA 6 sites mix official facts, trailer guesses and rumours in one sentence, and the reader cannot tell which is which. This one separates them. Every factual statement is its own record, with one of four statuses (confirmed, reported, rumor, debunked), a list of sources and what each source implied when it appeared.
Articles point at those records instead of retyping them. When a rumour is confirmed or killed, the record changes once, and every page, feed and widget that cites it follows. A debunked claim stays visible, struck through. A new date is a new claim, never an edit.
The tagline is “facts with receipts”, and it is literal: the point of a receipt is that you can check the claim without trusting the shop.
- 01
The claim as the unit
Each claim has a statement, a status, sources and an append-only history. Code enforces the rules: the last history entry must equal the current status, each step must start where the previous one ended, and a reference to a claim that does not exist cannot be saved.
- 02
Everything derived, nothing hand-kept
The changelog, RSS, the corrections page, sitemaps, llms.txt and a Markdown twin of every page are generated from the same records, so nothing can be quietly left out.
- 03
An admin panel that publishes without a deploy
Every section is editable in seven languages. A status change demands a one-sentence reason, which becomes the public changelog line, and the save is refused without it. Every save validates the whole site as it would look afterwards.
- 04
Seven languages
English, German, French, Spanish, Japanese, Chinese and Hindi. Translation runs through Claude with one instruction above all: do not add, soften or strengthen any claim. Machine translations are labelled as such on the page.
- 05
Open to machines on purpose
llms.txt, a JSON endpoint per claim, per-language feeds, ClaimReview and other structured data on every page type, and an embeddable widget in a sandboxed iframe that shares no code or styles with the host page.
How it fits together
Sources
- Official channels
- Footage and presswith what each implied at the time
The ledger
- Claimstatus, sources, history
- Validationthe whole site, on every save
Citations
- Status plateson pages, by id
- [[claim:id]]live status inside prose
Everywhere else
- Changelog, RSS, corrections
- Widgets, API, llms.txt

The part that never makes it into a portfolio. All of it happened, and all of it is in the commit history.
Fault 01
The ledger’s first real test caught the ledger
- Symptom
- On 29 October the ledger gained a claim that Rockstar had not named a composer. Status: confirmed, checked against two posts. The same day, Rockstar named one.
- Cause
- Nothing was wrong when it was written. In the site’s own words, it was true when entered and stopped being true within a day.
- Fix
- The next day it went Confirmed → Debunked with a dated reason, and I checked one claim end to end: claim page, Markdown twin, JSON, changelog, RSS, corrections, widget, llms.txt, Japanese. All agreed. It is the first real test, not a track record, and it shows where the guarantee stops: a fact cited by id follows the ledger, a fact typed into a post does not. Claims propagate, prose does not.
Fault 02
A 1.5-second budget, and a fix retracted in 7.5 minutes
- Symptom
- The budget was written into the design: LCP under 1.5 s on slow 4G. Early numbers looked great because they predated the photos.
- Cause
- The first fix was wrong, and the record says so:
content-visibilitydid not hold back the gallery’s image requests, and 7.5 minutes later a commit took the claim back and logged the real numbers. The photo itself weighs 17–27 KB. The cost is arithmetic: latency, then a queue behind the document, two fonts and the framework. - Fix
- None that hides it. A photo in the hero was chosen knowingly and the miss was written down. Measured today: text pages 1.14–1.38 s, inside budget; pages that lead with a photo 2.2–2.4 s, over it. Most sites bury a miss like this. This one has a public trail of a wrong fix, a retraction and a stated trade-off.
Fault 03
A safety check that became an outage
- Symptom
- A news post could fail with an HTTP 500.
- Cause
- A bad claim reference was made a hard error on purpose, and for files that is right: it fails the build and nothing reaches a reader. News posts live in Postgres and never pass through a build. The same
throwmoved from build time to request time, and a typo became a live 500. - Fix
- One rule became two. At render time, never throw: drop the reference and warn. At save time, refuse, and suggest the nearest real id by edit distance. Where a check runs matters as much as what it checks.
Fault 04
Making a “fully static” site editable without a deploy
- Symptom
- Three weeks before the game’s release, only news could change without a build. Every correction to a claim meant a commit and a deploy.
- Cause
- A rebuild per edit is slow and heavy. Moving everything into a database throws away git’s review and rollback. And a build cannot see a database, so prerendered pages would show the old version over an editor’s change.
- Fix
- Three layers. Files in git are the base. A Postgres row keyed by the exact file path replaces that file, so the rest of the system does not know the database exists. An in-memory overlay serves requests. Prerendering was given up, and “fully static” with it. Result: 63 of the 85 live claims exist only in Postgres, added within two days of that commit, with zero CMS dependencies.
Fault 05
Seven languages, one layout, no webfont
- Symptom
- Japanese, Chinese and Hindi had to fit a design built on tall condensed caps, tight tracking and a 0.92 line-height, inside the performance budget.
- Cause
- A CJK webfont costs megabytes, and the type treatment that defines the look damages those scripts: negative tracking crushes them, and a 0.92 line-height collides with Devanagari. Then the assumptions that only hold in English: word order in templates,
toLowerCase(), title length counted in characters. - Fix
- System fonts per language, taller line-heights, no negative tracking, Japanese headlines breaking between phrases, title width measured in pixels and word-order-safe templates. Measured result: the Japanese home page weighs 3 KB more than the English one, with the same LCP. The work was removing assumptions, not adding fonts.
- claims in the ledger42 confirmed, 34 reported, 4 rumor, 5 debunked; 30 Oct 2026
- claims added through the admin in 48 hoursno commit, no deploy
- URLs across the sitemapsseven languages, 43 news posts
- Japanese page against the English oneLighthouse: 424 vs 421 KB
- accessibility, best practices, SEOLighthouse on five pages, every run; performance 95–100
- commits on 8 active days22 of 23 CI runs green: lint, types, site-wide validation, JSON-LD crawl. No unit tests
Commit pulse
Every commit in the project’s repositories, on one line. The taller the bar, the hotter the stretch.
- The engine, in one evening: claims, history, eight page types
- Seven languages, 31.5 minutes apart: infrastructure, then content
- thecity.gg goes live on its own domain
- News 500 fixed, sitemaps and redirects
- Every section editable without a deploy
If what you publish has to stay true after you publish it, make the fact the unit, not the page. The same structure fits price lists, compliance statements and product specs: anything that goes stale in ten places at once. If that sounds like your site, tell me about it.
Or ask the digital mind about it