drive-between-the-lines/README.md
dejvino d10033d79e Phase 8: recognition, pursuit, and losing them
Cover is now a state you can lose. Suspicion builds while enemy eyes are on you,
squared with proximity so one patrol beside the car outweighs a crowd across the
street, and blocked by buildings like everything else. It cannot build at all
behind your own lines. Ramming, being shot at and running somebody over skip the
meter and land in full.

At the top of it you are hunted: nearby enemies drop what they were doing and
come, and keep joining as you drive past fresh patrols, since a chase you could
outrun by passing more of them would be no chase. Being hunted is also now the
only reason the enemy shoots at you — a target is a state, not a property of the
road you happen to be on.

Two ways out. Break line of sight for fourteen seconds, with any glimpse
resetting the clock; hunters drive to where they last saw you rather than where
you are, so turning a corner and changing direction is the move. Or let your own
side occupy them — hunters are patrols, not fanatics, and enough peeling off
ends the chase outright. Escaping leaves suspicion at 0.6 rather than zero: they
know there is a car worth looking for.

Tuning a test caught: the post-escape value sat a hair above the "suspicious"
threshold, so the afterglow expired within a second and the rule may as well not
have existed. Decay slowed to match, so cover is earned back by being dull
rather than by turning one corner.

Verified end to end in the running game: loitering beside a patrol in occupied
ground filled the meter and pulled three hunters, fleeing out of sight cleared it
after exactly fourteen seconds, and a visible player was closed on from 61m to
12m while taking hits.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 12:20:47 +02:00

562 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Drive Between the Lines — prototype
Endless driving survival game.
- **Phase 0** — a drivable car with persistent wear that is felt through the wheel.
- **Phase 1** — a road network whose roads remember being driven, and escalate.
- **Phase 2** — bases, a quest board, and known-vs-new target selection.
- **Phase 3** — regional control: behind your own lines, nobody is watching.
- **Phase 4** — the front line moves, from your work and on its own.
- **Phase 5** — four distinct jobs, radio texture, and work found in the field.
- **Phase 6** — an inhabited world: traffic, people, patrols, and a live war.
- **Phase 7** — sound, synthesised from the simulation.
- **Phase 8** — recognition: being noticed, being hunted, and getting away.
## Running
```bash
npm run dev
```
Then open http://localhost:5173. Append `?seed=anything` to regenerate the world —
seeds may be numbers or words.
| | |
|---|---|
| `W` / `S` | throttle / brake-reverse |
| `A` / `D` | steer |
| `Space` | handbrake (rear wheels only) |
| `R` | respawn the car — **does not** repair it |
| `1``3` | accept a mission, when parked at a base |
| `M` | sound on/off |
| hold `R` | wipe the campaign and start over (5 seconds) |
Progress saves itself every 20 seconds, and immediately whenever you take or
finish a job. Saves are per seed; `?seed=x` is a separate campaign.
Drive to a green beacon (a base) and stop. Take a job, drive to the amber
beacon, stop and hold position for three seconds, then return to **any** base —
the amber beacon goes out once the job at the target is done, since the target
is no longer anywhere you need to be.
```bash
npm test # sim + headless physics
npm run build # typecheck + production bundle
```
## Road heat
Every metre driven **on or alongside** a road adds heat to that segment; every
road everywhere sheds it slowly. Cross a threshold and the road escalates:
`Clear → Patrol → Barricade → Turret`
- **Patrol** sends a car to work the road for a while, which cools it back down.
- **Barricade** sends an engineer to pour concrete across it, leaving a gap you
have to slow for.
- **Turret** sends another to put up a tower, and a gunner to man it.
None of that appears on its own — see [A world with people in it](#a-world-with-people-in-it).
Roughly four traversals take a road from clear to turret; an untouched road
cools off in about four minutes. Both numbers are in `sim/heat.ts` and both are
guesses meant to be tuned.
### District heat
Roads remember being used. Districts remember *you*.
Road heat alone rewards a strange kind of play: work one part of town hard but
take a different street every time, and nothing ever gets hot. So there is a
second, coarser pool. Every metre you cover inside a district raises its
standing — on-road or off, whether or not you touch a road at all — and that
bleeds into every road running through it, including ones you have never driven.
- It **accrues from being there**, so cutting across country is not a loophole.
- It **cannot fortify a road on its own** (`AREA_INFLUENCE` sits below the
barricade threshold). A hot district gets roads patrolled; concrete still
takes somebody actually using that road.
- It **outlasts road heat**. A road is re-secured by one patrol; a district's
reputation takes far longer to fade.
- A patrol working a road **does** settle the district around it, at about a
tenth of the rate it settles the road. Any faster and waiting out a patrol
would launder your reputation, and district heat is meant to be the part you
cannot patrol away.
### Speed is conspicuous
Heat scales with how fast you were going, not just how far. A car at walking
pace is traffic; a car doing ninety through a checkpointed district is the thing
people phone in. Roughly a 2.2× spread between crawling and flat out, and it
stops rewarding ever-higher speed past a point so the answer is never one exact
number on the clock.
Together with the road classes this is the real routing decision: the trunk fast
and noticed, or the lanes slow and ignored.
### The road hierarchy
Roads are not all the same road:
| Class | Tarmac | Speed | Attention |
|---|---|---|---|
| Track (single car) | 4.5 m | ×0.55 | ×0.55 |
| Road (two lanes) | 9 m | ×1 | ×1 |
| Trunk (four lanes) | 15 m | ×1.35 | ×1.35 |
That trade is the network doing real work rather than being the floor you drive
on: a trunk is fast and direct and exactly where anyone looking for you would
look; a lane is slow and awkward and nobody is watching it. Trunks run the whole
width of the map and are never thinned away, so there is a shape to learn — get
onto the trunk, then work down the lanes — instead of a uniform mesh where every
road is as good as every other. Route previews are costed by **travel time**, not
distance, so the board's estimate matches the way a driver would actually go.
### The route corridor
Heat is credited to a road for anything within its corridor — half the tarmac
plus a 7.5 m verge, so it scales with the class — not just the tarmac itself.
Otherwise the dominant strategy is to drive *next to* every road and never
accrue heat at all.
Two consequences follow, and both are deliberate:
- **The cutoff is hard, not a taper.** A taper leaves a gradient to optimise
along — sit at 80% of the corridor, take 20% of the heat. A cliff edge means
you either use the route or you actually leave it.
- **Barricades span the full corridor**, well past the kerb. A checkpoint that
stopped at the tarmac would be free to round on the verge at speed.
The 7.5 m verge is about two car widths, so hugging the shoulder gains nothing,
while leaving most of the map genuinely off-route. Widening it backfires: make
the corridor too generous and heat becomes unavoidable everywhere, and "take a
different route" stops being a choice at all.
### Tarmac versus everything else
Roads have to be worth using, or the hierarchy above is decoration. Off the
route the car makes about 40% of its road top speed, loses grip, and scrubs off
momentum in a fifth of the distance. It is a real cost, deliberately not a wall:
going around a checkpoint cross-country is a legitimate move, and an earlier
tuning that made off-road a 12 km/h crawl removed the choice rather than pricing
it.
### Buildings, and why they are dense
Off-road used to be open country, which made every barricade optional — you just
swung wide around it. Buildings are now laid out on a jittered lattice (random
scatter cannot pack boxes and saturated at a third of the target), which takes
bypass lanes around a checkpoint from none obstructed to about two thirds. You
can still get around one; it means threading a maze at walking pace instead of a
free detour at speed.
### Other deliberate choices
- **Concrete never closes around the car standing in the gap.** A block spawning
on top of you would put a static collider inside the chassis, so the last slab
waits until you move.
- **A checkpoint's position is seeded from its segment**, so a given stretch of
road always fortifies in the same place. Recognising it is part of learning
the map.
The road network is a jittered grid with roughly a quarter of its edges removed,
keeping the whole thing connected. The loops are the point: "take a different
road this time" is not a decision unless alternative routes exist.
## Missions and intel
The board at each base offers a mix of **known targets** and **new** ones, and
describes the route to each in words rather than numbers — "barricaded · last
word 6 min ago", "no one has been down that way".
The important part is *where those words come from*. The board never reads heat
directly. It reads `sim/intel.ts`, which is a snapshot of what the player has
actually observed and when. Drive a road and you write down what was on it;
leave it alone and your note ages while the road keeps escalating without you.
So a known route is genuinely knowable, genuinely stale, and never a guarantee —
which is the tension the brief asks for.
**Recon missions** are the tool for converting a new target into a known one:
completing one surveys every road within 130 m, filling in the map without
having to drive each one.
Four jobs run end to end, and they differ in **pacing and risk**, not just in
the noun on the board — otherwise four mission types is one mission type with
four labels:
| Job | At the target | The risk |
|---|---|---|
| **Supply run** | 3s unloading | the drive out |
| **Retrieval** | 5s loading | the drive *home*, with goods that break |
| **Intel drop** | 9s transmitting | a long wait, stationary, in the open |
| **Survey** | 2s | cheapest on site; the payoff is the map |
Retrieval is the one that carries something home, and impacts damage it. It pays
the most, but it pays for what *arrives*, not what you set off with. Hand-in
works at *any* base, not the issuing one, because the brief is explicit that a
blocked road home must never strand the player.
### Work you find rather than work you are given
Past the line, and only when you have nothing in hand, someone occasionally
turns up: a wounded stranger, a contact flashing headlights, a wreck worth
stripping. They are deliberately **not** on a board — you cannot plan a route to
one, compare it against anything, or shop between them. You take the detour in
front of you or you drive on.
### Radio
Chatter that frames who you are — an undercover driver being talked at by people
busy elsewhere. Every line is triggered by something actually happening in the
sim: crossing a border, a road you have made notorious, a car past saving, or
the front genuinely drifting your way, which is the brief's "the enemy has other
priorities" said out loud without ever showing a number.
### Rewards, and why repairs exist
New targets pay `NOVELTY_BONUS` (1.8×) more than known ones. That multiplier is
the phase's tuning dial: if you never take the new target it is too low, and if
you never take the known one it is too high.
Payment is in **parts**, which repair the car — the only thing in the game that
pushes condition back up. Repairs go to the worst subsystem first and stop at
its **ceiling**, which drops permanently with every bit of damage taken
(`PERMANENT_SHARE`, currently 30%). So a car can be patched back to what it is
still capable of, but never to what it was. The HUD bars show this directly: `█`
is current condition, `·` is what repairs could still reach, `×` is gone.
This is what gives the board's reward a reason to matter. Without an economy the
choice between a safe route and a risky one has no stakes; with one, the risky
job is what keeps the car alive a while longer.
## A world with people in it
Up to Phase 5 the world was scenery. Heat poured concrete on a road and that was
the whole of the enemy. `sim/units.ts` is the layer that makes it inhabited.
**Enemy ground is patrolled because it is enemy ground.** There are standing
patrols on the roads wherever you are past the line, and more of them the deeper
in you go — none in liberated territory, one in contested, three in occupied,
five on the frontier. Nobody summoned these; they are what hostile territory
looks like.
**On top of that, nothing appears — things are sent.** When a road turns
notorious a patrol is *dispatched*: it starts at a junction a few hundred metres
away, drives there, sweeps up and down for about a minute, and while it works the
road it brings the heat back down. Then it leaves. The enemy is not punishing
you; they are re-securing a route and going home.
**Everything on a road is built, including the concrete.** At `barricade` an
engineer is dispatched and the blocks go up over about twenty seconds of someone
standing there; at `turret` another is sent and the tower takes forty. Arrive
mid-build and you find a half-finished stump with nobody on it. Only once the
tower stands does a gunner man it, up on top so he shoots over his own barricade.
Let the road cool and the whole thing is abandoned.
**Traffic and people.** Civilian cars route across the network with somewhere to
be; people wander on foot near the buildings. Both scatter when shooting starts.
### The war you are driving through
Skirmishes break out between the two armies on contested and occupied ground,
whether or not you are anywhere near. They are not a set piece for the player —
you are not invited.
Rounds are **real objects in flight**, not instant hits resolved between two
combatants. That is the whole point: if shots resolved instantly, driving through
a battle would cost nothing, and "the only road to the target runs through a
firefight" would not be a decision. A stray round does not check whose war it is,
so it will hit a bystander, a civilian, or you.
Buildings stop bullets, which is what makes cover worth anything. Hits are routed
through the same wear model as a crash, so being shot costs you ceiling too — it
is permanent in the same way everything else is.
**The enemy does not shoot at you by default.** You are an undercover driver, not
a target. They open up only past the line *and* on a road they have already
checkpointed — that is, a road you personally made notorious.
## Being recognised
You are an undercover driver, not a combatant, and that only means anything if
cover is a state you can *lose*.
**Suspicion** builds while enemy eyes are on you — faster the closer they are,
squared with proximity, so one patrol beside the car matters more than a crowd
across the street. Line of sight is real: buildings block it. Behind your own
lines it cannot build at all. Some things skip the meter entirely and land in
full — ramming a vehicle, being shot at, and above all putting somebody under
the wheels.
At the top of the meter you are **hunted**. Nearby enemies drop what they were
doing and come after you, and they keep recruiting as you drive past fresh
patrols — a chase you could outrun by passing more of them would be no chase.
This is also the *only* thing that makes the enemy shoot at you: being a target
is a state, not a property of the road you are on.
**Getting away** takes one of two things:
- **Break line of sight for fourteen seconds.** They drive to where they last
saw you, not to where you are, so turning a corner and changing direction is
the move. Any glimpse resets the clock.
- **Let your own side occupy them.** Hunters are patrols, not fanatics: an
insurgent squad on their doorstep is a better use of their time. Enough of
them peeling off ends the chase outright.
Escaping does not hand your cover straight back. Suspicion drops to 0.6 and
decays slowly from there — they know there is a car worth looking for, and you
have to be dull for a while to be forgotten.
## Debug
Load with `?debug=1` for a `window.__dbtl` handle and a small panel in the
corner. The panel has `` / `+` buttons for the car's condition, because
"drive a wrecked car for a minute" is something you want to do *while driving*
rather than while typing into a console.
The handle also steps the simulation by hand, renders on demand, and writes
frames to `shots/` via a dev-only endpoint — which is how this build gets looked
at at all when the tab is not compositing. `__dbtl.state` exposes the live sim.
## Sound
Everything is **synthesised** — there are no audio files. The build has to stay
self-contained, so the engine is detuned oscillators, the tyres and gunfire are
shaped noise, and the ambience is a filtered drone. That constraint turned out to
be an advantage: sound generated from the simulation can carry the simulation's
state rather than just decorating it.
- **The engine has gears**, so the note climbs and drops rather than rising
forever with the speedometer.
- **A worn engine sounds wrong**, not just slow: roughness rises and the top of
the rev range falls away as condition drops. The car's decline is audible.
- **Bald tyres squeal sooner** than good ones, off the same lateral slip the
physics actually reports.
- **Gunfire is positional** — it falls off with the square of distance and pans
as you turn, so a firefight two streets away is a direction, not just a noise.
- **Territory has a tone.** The drone drops and thickens the further past the
line you are, matching what the palette does visually.
The split is deliberate: `audio/mix.ts` holds every decision as plain numbers
and is unit-tested; `audio/audio.ts` is only Web Audio wiring, which cannot be
tested outside a browser. Browsers refuse to make noise before the user has
interacted, so the context is resumed on the first keypress or click.
## Regional control
The map is cut by a single **front line** into four bands, running from your own
territory out past the edge of the known world:
`Liberated → Contested → Occupied → Frontier`
| Band | Heat accrual | Heat decay |
|---|---|---|
| Liberated | **×0** | ×4 |
| Contested | ×0.55 | ×1.4 |
| Occupied | ×1 | ×1 |
| Frontier | ×1.3 | ×0.8 |
Liberated accrual is exactly zero, not merely small. A trickle would eventually
fortify your own back garden, which is nonsense, and it would mean the safe area
quietly punished you for using it. Behind your own lines, nobody is writing down
which roads you take.
Decay is judged by whoever holds *the road*, not whoever holds the ground the
car is standing on — your own side dismantles checkpoints on its own roads
whether or not you are there to watch.
### The line moves
Two forces, and they are independent by design.
**Your work.** Completing a mission pushes the front forward, weighted by what
the job was worth — a long haul into unknown country moves the war more than a
local errand, and supply runs move it more than surveys. Ground won is not held
forever: the player's contribution decays over about seven minutes, so the enemy
presses back on its own.
**The war elsewhere.** The line also drifts with nobody's help, in both
directions. This is not background flavour: the brief is explicit that ambient
drift is what makes the setting a war rather than a level, and that it is the
*only* mechanism for the enemy's attention being pulled elsewhere — there is no
separate "baseline threat" system alongside it.
Drift is a sum of slow waves rather than a random walk. A walk either wanders
off the map or, once reined in, moves so little the war reads as static. Waves
at incommensurate periods are unpredictable in practice, always bounded, and
reproducible from the seed — so "the region changed while you were away" is
something a test can assert. Most of the movement is shared by every boundary,
because a front that advances advances as a whole; each band then wobbles a
little on its own so the map is not one rigid object sliding about.
In practice a boundary swings about ±45 m and a spot near a border changes hands
several times over a quarter of an hour.
**Nothing announces any of this.** There is no notification, no readout, no
number. The minimap shows who held each patch of ground *when you last stood in
it* — so after a while away, your map is simply wrong, and the only way to find
out is to go back and look.
Your own ground is a pocket, not the map: roughly 15% liberated, 25% contested,
27% occupied, 33% frontier. The starting base sits at the friendly *edge* rather
than in the middle, because starting central put half the world harmlessly
behind your own line where nothing watches and nothing happens. Those splits are
measured, not guessed — the bands are strips across a square with the axis
running diagonally, so area is nowhere near linear in depth.
**You are never told which band you are in by the world itself.** Crossing a
border eases the fog, the light and the palette: your own ground is open and
green, contested goes flat and hazy, occupied turns dusty and closes in, the
frontier is cold and short-sighted. The HUD names the band for now, but that
line is debug scaffolding for the same reason the heat numbers are — Phase 3's
gate is telling them apart *without* being told.
## The map
A sketch map, top right, drawn from `Intel` rather than from the world:
- **The road network's shape is always visible**, as faint dashed lines. You are
an operative with a map, not an amnesiac.
- **Roads you have actually seen** are drawn solid and coloured by the heat you
remember on them — which is what you last saw, not what is there now.
- **Ground you have driven through** is filled in, tinted by who held it *when
you were last there*. A survey mission fills in a whole area at once.
- **The border** is drawn between adjacent remembered cells that you recall as
belonging to different sides — never from the front's true position, which
would hand you a live readout of a line that is supposed to move behind your
back.
- **Old notes fade.** An observation drawn faint is one you should not trust.
Nothing on the map reads live world state. Heat is what you last saw on a road;
territory is who held it when you last stood there. Both keep changing after you
leave, and the map has no way of knowing.
The HUD also carries an eight-point compass arrow to the active target, since
the map alone does not tell you which way to point the car.
## Layout
The one rule worth keeping: **`src/sim/` imports neither three.js nor Rapier.**
Phases 14 (heat, regions, front lines, quests) are all simulation, and keeping
them engine-free is what makes them unit-testable and fast-forwardable — you can
run a hundred simulated days in milliseconds to tune an escalation curve without
ever opening a browser.
```
src/
sim/ pure model, no engine imports:
world, roads, heat — the map and its memory
radio, opportunities — texture: chatter and field work
units, combat — traffic, patrols, soldiers, rounds
save — the campaign as JSON
regions — the front line and who holds what
intel, routing, quests — what the player knows and is asked to do
bases, car — where missions come from, and decline
physics/ Rapier world, raycast vehicle, input → wheel forces
render/ three.js scene, roads, markers, chase camera
core/ fixed-timestep loop, seeded RNG, keyboard
ui/ debug HUD, quest board, sketch minimap
audio/ mix (pure: what it should sound like), audio (Web Audio wiring)
persistence.ts integration layer: saves in localStorage
debug.ts ?debug=1 handle: step, render and capture frames to shots/
carSpec.ts shared car dimensions, so body and mesh cannot drift apart
heatProps.ts integration layer: heat levels → colliders + meshes
```
`heatProps.ts` sits at the top level on purpose — it is the one module allowed to
touch both Rapier and three.js, because it owns objects that must exist in both
or neither. Its tests run headless: three.js scene graphs work fine in Node, so
"the barricade's collider was removed along with its mesh" is a unit test.
Data flows one way: `sim``physics``render`. Condition reaches the physics
layer already digested into a `Handling` by `deriveHandling`, so there is exactly
one place where "how broken the car is" turns into "how it drives".
## Notes on the state of it
- **The car handles like a placeholder.** Suspension, grip, and engine numbers in
`physics/physics.ts` and `sim/car.ts` are first guesses that pass a smoke test,
not something tuned by feel. That tuning *is* Phase 0's verification gate.
- **Wear rates are deliberately aggressive** so decline is visible in minutes
rather than hours. Turn them down in `applyWear` once the curve reads right.
- **No interpolation** between physics steps. Fine at 60 Hz; revisit if the step
rate changes.
- **Nothing is persisted** across reloads — condition, heat, intel and completed
runs all reset. IndexedDB comes with the campaign layer, and `sim/intel.ts`
plus `sim/heat.ts` are the two things that most need it.
- **Repairs are automatic on hand-in.** Parts get spent the moment you report
back. The brief wants repair to be a physical, on-foot act with scavenged
parts; that is Phase 6.
- **The board's route preview assumes the shortest path.** If you drive a
different way, the intel you were shown described a route you did not take.
`findRoute` already accepts a cost function, so a "safest route" preview is a
small change when it is wanted.
- **Junctions have no heat of their own.** A point near a junction is credited to
whichever segment is nearest, so a heavily used crossroads never fortifies as a
crossroads. Known gap, deferred on purpose.
- **Barricades are visually crude.** Blocking the whole 24 m corridor means two
long concrete slabs, which reads more like a wall than a checkpoint. The
gameplay shape is right; the presentation wants berms, wire, or wreckage on the
outer sections. They can also clip scenery placed on the verge — both are
static bodies, so physics is unaffected, but it looks wrong up close.
- **Heat has no diegetic signal at a distance.** You learn a road is hot by
arriving at the checkpoint. The brief wants it readable from patrol density and
wreckage before you commit — that needs the enemy presence Phase 5 brings.
- **The heat HUD lines are debug scaffolding.** The brief is explicit that no
numeric heat meter ships; they exist to tune the curve and should come out.
- **Bundle:** ~2.7 MB raw / ~945 KB gzipped, dominated by Rapier's WASM, which
`rapier3d-compat` inlines as base64. Switching to the non-compat `@dimforge/rapier3d`
package serves the WASM as a separate file (~570 KB gzipped, compiled in
parallel with the JS) at the cost of extra Vite plugin config. Worth doing
before anyone but you plays it; not worth doing now.
## Next
Two gates are open at once, and both need a person, not a test.
**Phase 1:** you catch yourself avoiding a road because of its history, not its
distance. If it fails, check `METRES_PER_HEAT` (escalation too slow to matter in
one session), `DECAY_PER_SECOND` (nothing accumulates), or whether the props are
actually inconvenient enough to route around.
**Phase 2:** you take the new target a meaningful fraction of the time, for
reasons other than curiosity — because the known one has genuinely got
expensive. If the known option is always strictly better, raise `NOVELTY_BONUS`.
If you never take a known target, lower it.
**Phase 3:** driving into an occupied region, you can tell it from a liberated
one within a couple of minutes, from tone alone, without reading the HUD line
that names it. If you cannot, the palettes in `render/scene.ts` are too close
together — push them apart before trusting the band boundaries.
**Phase 5:** someone coming to this cold can describe what kind of game it is
and who they are playing after one session, without the premise being explained
first. If the four jobs still feel like one job with four names, the dial is
`MISSION_SHAPE` — push the work times and the cargo risk further apart before
adding anything new.
**Phase 4:** returning to a region after time away, you are genuinely unsure
what you will find until you are back on the ground — and when it has changed,
it reads as the world having moved rather than as a notification you were given.
If the world instead feels arbitrary, the drift is too fast: lengthen the wave
periods in `sim/regions.ts` before shrinking their amplitude, since it is the
*rate* of change that reads as noise, not the size of it.
**Phase 6:** the world should feel lived in before it feels dangerous — traffic
and people you have no reason to interact with, and a war that is obviously not
about you. If firefights read as encounters staged for the player, the spacing
and spawn rules in `sim/units.ts` are the dial.
Still deferred: a deeper repair economy
with scavenged parts and on-foot work, car identity and disguise feeding into
heat, and expanded combat. Nothing there should be pulled forward until the loop
below it is proven.
The most useful thing before any of that is taking the debug lines out of the
HUD. Several gates now depend on reading the world rather than a readout, and
the scaffolding actively works against you when you sit down to judge them.
Watch for one failure mode in particular: if you find yourself taking whichever
job is *nearest* and ignoring the intel line entirely, the board is decorative
and the phase has not landed, whatever the reward numbers say.