The first job you can clock on to, and the truck that drove through the river
You get woken at half nine. A prompt asks whether you are going to work or staying in bed. Say yes and you are in the passenger seat of a council flatbed with a bloke called Wozza driving. He talks at you on the way out to the estate. The truck stops at a fly-tipping hotspot, you get out, and you carry dumped rubbish — mattresses, bags, a carpet roll, a dead television — one item at a time to the back of the truck and load it. The truck waits as long as you take. Three stops, then it runs on to the incinerator, tips the bed, and drives you home. Then you get paid, per item and per site, with a bonus if you cleared everything.
That is the first job in this game a player can work from clocking on to being paid for it, and most of it has now been driven in a live play session with the user watching. Everything after this one — the bin round, the pub cleaning shift — copies its shape.
The job was already in the fiction, and nobody had noticed
The most useful thing that happened on this build happened before any of it was built.
The game has a job centre with an advisor behind desk four and a vacancy list. That list had been written weeks earlier and had three posts on it. One of them read Fly-Tipping Clearance Assistant, city council environmental services, £2.25 an item plus £8 a site. The employment state machine behind it — a job code, an accept function, a pay multiplier tied to an assessment band, a sign-on status — was all written and all sitting there unused.
The workmate existed too, as four words in the advisor's script: "back gate, ask for Wozza", followed by a line about a grabber and a tally sheet, and a warning not to lose the tally sheet. That was his entire existence. No character, no assets, no cast row.
So the build was made to fit what had already been written rather than inventing a parallel employment system beside it. The job is job code 2. The rate is £2.25 an item and £8 a site, because that is what the vacancy advertises. The tally sheet is where the per-item score comes from. Wozza is the workmate, not some new storeman invented to stand next to him.
Two things in the written fiction disagreed with the built world, and both were settled by changing the writing rather than the world. The advisor's script gave the depot an address in a village that the depot is nowhere near; the built depot sits out past the incinerator off a service road. Her line changed, the building did not. She also described the vehicle as "the yellow van" from an earlier design; it is a flatbed lorry now, so the line says flatbed. Neither of those is a hard call, but making it explicitly — the world is the ground truth, the script is the thing that moves — is what stops a project accumulating two contradictory canons.
One detail worth stealing: the money in her pitch is generated from the same constants the job pays out, never typed into her dialogue. If the rate changes, her sales patter changes with it. There is no second place for the number to go stale.
Eight minutes is the whole working day
The game clock runs at one real second per game minute. A shift advertised as 10:00 to 18:00 is therefore eight real minutes, and that number destroyed the first design.
Measured against it: the depot is about a kilometre from the estate, so the commute out and back eats roughly two and a half minutes. Visiting all eighteen fly-tipping hotspots is another two and three-quarter kilometres by road, call it four minutes more. That leaves about ninety seconds to clear sites whose carry distances run to thirty-three metres. Not a shift — a joke.
Three things came out of redoing that sum properly:
- The round is a rotating subset, not the full sweep. It rotates on a day counter rather
than by weekday, because the game has no concept of weekdays — a Monday-to-Friday job would shut the depot two days in seven with nothing on screen explaining why.
- The truck runs two speed classes: sixty on the trunk run out to the estate, forty
inside it. A single speed does not fit. The trunk leg is sixty per cent of the driving, and at one flat speed the round leaves ten to twenty seconds per stop, which is not a race, it is a formality. Two-tier leaves two to three and a half minutes of the shift free for actual hauling.
- Every one of those numbers is a Blueprint variable, not a literal, and the arithmetic is
written down where the next person will find it, with an instruction to re-run the sum before touching the hours, the speed or the number of stops.
The trap: the truck drove through the river
Search-box version of the symptom: my AI vehicle ignores the road network and cuts straight across country. It fords a river. There is a bridge fifty metres away and it drives past it.
The user watched a ride and reported it in one sentence: mostly sticking to roads, but it went through the river at one point where it should have used a bridge — "which is also what the taxi did."
That last clause is the whole diagnosis, and it arrived free. Two unrelated vehicle systems, built months apart, producing the same wrong behaviour, means the fault is not in either of them. It is in the thing they share.
Why it happened
Routes are baked offline as: start point → join the road graph → travel along roads → leave the graph → end point. The joins at each end were straight lines. That is fine when the join is short and catastrophic when it is long, and it was long for a reason nobody had looked at.
The graph builder is shared, and it was written for the police patrol. It keeps a road only if that road carries one of three tags, or is one of three hardcoded connectors. Those tags encode a design decision that has nothing to do with lorries: these are the streets a beat car ought to be seen driving down. Every vehicle system in the project inherited that definition without anyone deciding to.
It threw away twenty-nine of sixty-two roads — all eight arterials, all seven roundabouts, and, with a certain grim comedy, the service road that had been built specifically so the truck could reach the incinerator. With the trunk network deleted from the graph, the depot sat 248 metres and the incinerator 341 metres from anything the router considered drivable. Both ends of every round fell back to a straight chord. Those chords crossed the river.
There was an earlier, narrower version of the same bug that had already been fixed and had felt like the answer: two points on the same road with no junction between them collapse to a single graph node, so the router returned a straight line between them and the run home cut a 144-metre diagonal across the field beside its own road. Fixing that special case made the visible symptom rarer without touching the cause, which is exactly the sort of fix that buys you a week of believing you are done.
The fix, and the guard
Two changes, both offline, neither of them touching the police module:
- Tag lorry-legal roads before handing them to the shared builder. The coverage
definition stays the police's business; the refuse round supplies its own and the builder is none the wiser.
- **Snap a route's endpoints to the nearest edge, and split it** — rather than to the
nearest junction. A junction can be hundreds of metres from a depot gate. A point on the carriageway outside it cannot.
The guard is a scorer that runs outside the editor, takes two baked route files and prints the delta: total off-road distance, and the number of river fords. Before: ten fords, and the 95th-percentile off-road stretch 83.5 metres. After: zero fords, zero unbridged crossings, 95th-percentile off-road 0.0 metres. A separate diagnostic tool splits "this road crosses the river" from "this route fords it", because those are different faults with different owners.
Two measurement traps came out of the same work and are worth more than the fix:
- Do not judge this by the percentage of waypoints that are off-road. It read 28.6% and
was mostly correct — fly-tip parking spots are on verges by design. Judge the per-leg flags, not the aggregate.
- The road verifiers are two-dimensional. They report a crossing as unbridged whenever
the bridge exists but sits at a different height, so "road X has no bridge" is a claim to check against the bridge manifest before believing. The inverse is worse: a deck that does not actually touch its own road silently clears a crossing that still fords. The verifier grew an explicit "does this bridge carry its road" check for exactly that reason.
What was deliberately not fixed
The taxi and the satnav share this precise cause and were left alone. Editing three systems in one pass, on the strength of one system's before-and-after, would have been a silent change to two things nobody had measured. They get their own fix with their own verification. That is the whole teachable point of the bug: the fault belongs upstream of every vehicle, so the repair belongs upstream too — not forked into each one where three copies can drift apart.
The bloke in the driver's seat
Wozza is the reason the ride is worth sitting through, and he cost more than the shift did.
120 baked voice lines. The written script has 156, but 36 of them are the player's multiple-choice replies and the player character has no voice — baking those would have spent about a third of a metered quota on a silent role. His voice is another character's voice pitched down one semitone, which is how one project gets more distinct speakers than it has voice slots.
A driver rig, not a driving animation. His feet are solved onto the actual cab floor with two-bone IK, his hands onto the actual steering rim, and the steering angle is derived from the truck's own yaw rate — the rig differences the parent vehicle's heading each frame, scales it, clamps it and smooths it. Nothing outside that one asset knows he exists; the shift controller was never told to push a steering value at him. Order in the rig matters: breathing and head-turn are solved before the hands, because the arms hang off the chest, and solving the grip first would let it drift every time he breathes.
A face that moves. Every one of his 120 lines has a matching facial animation baked from the audio, and one name addresses the sound, the face clip and the line id together — which is what makes "say line X" a single function rather than three lookups. Underneath it sits a hand-built blink loop: twenty seconds, no bone tracks at all, two curves, six blinks. Epic's own idle loop was rejected on purpose — it carries 875 bone tracks including spine, neck and head, so it would have fought the body rig for the skull, and the character's hair is bound to the face mesh.
Banter that starts below zero. The player begins at a trust of −1, not 0, because the band table puts the "outsider" range at −999 to −1 — so a starting value of 0 already reads as tolerated, and the three first-day exchanges could never have fired. Minus one is the lowest number that still means "new lad", and it is what makes him call you Sunshine until you have earned youth.
The banter watches the shift; it does not edit it. The obvious build was to splice "say a line" calls into the shift controller at start, arrival, tipping and clock-off. That was rejected, because every compile of that Blueprint costs a re-run of two seeding scripts on a system that already works. Instead the banter actor does rising-edge detection on the shift's own public variables each tick — running true, holding true, tipping true, sites cleared increasing, the per-stop timer past 60% of its target. Zero changes to the shift loop, zero new re-seed chores, no risk to something that plays. A watcher actor is nearly always cheaper than editing a fragile manager.
What is measured, and what has actually been played
The wake-up call, the seating, the eligibility gates, the pay and the alight-and-board rule were all verified in live play with the user watching. Pay landed exactly on the arithmetic: twelve items and four sites at a 0.90 assessment multiplier came to £53.10, a clean sweep of six sites to £68.20, and the wallet rose by exactly those amounts. The truck refuses to move off a stop while you are still working it. The banter was proved end to end in its own session — the right cue on screen, the right three replies, a choice moving trust from −1 to 1, and the head easing round to look at the player rather than snapping.
The honest gaps, in the project's own words: the carry-and-load loop has not been proved end to end in play. It passed a 36-check bench harness with no play session involved, and one attempt at proving it live missed because the test aimed the interaction ray by eyeballing a pitch angle instead of aiming from the eye point at the item's centre. The camera's yaw-follow in the cab has never been verified either — it has been outstanding for three sessions, and it is the kind of thing only a human watching can judge. The truck also cannot reverse, so it currently drives forward onto a tipping apron that was modelled for a reversing lorry.
What to take from it
- Read the fiction before you build the system. The vacancy board, the pay rate, the job
code and the workmate's name were all already written. Building to fit them cost less than inventing a parallel system would have, and produced a job that is already consistent with everything a player has been told.
- When the world and the script disagree, move the script. Geometry is expensive and
already verified; a line of dialogue is a string. Decide which one is ground truth once, out loud, and write the decision down.
- Generate the numbers a character quotes from the constants that pay out. A rate typed
into dialogue is a rate that will be wrong within a month.
- Two systems with the same bug do not have two bugs. "The taxi did that too" was worth
more than any amount of staring at the truck. Fix it upstream, and fix it for one consumer at a time with a before-and-after per consumer, rather than silently editing three.
- A shared abstraction carries the assumptions of whoever wrote it first. A road graph
built to describe where a police car should be seen is not a road network, and every later caller inherited that opinion without ever agreeing to it. Ask what a shared filter was originally for.
- Score the fix with a tool that takes a before and an after. Ten fords to zero is an
argument. "It looks better now" is not, especially when the obvious aggregate metric — percentage of waypoints off-road — barely moved and was never the right measurement.
- Say "measured" and "played" as separate claims. Bench harnesses and live sessions
answer different questions, and a build log that blurs them will cost somebody a day.