# Traffic Sandbox: rule reference

Model version: **junction-v19**. This is an illustrative model with feet, seconds, and MPH, not a calibrated traffic engineering prediction. Product requirements are in [PROJECT-INSTRUCTIONS.md](PROJECT-INSTRUCTIONS.md).

## Where to review changes

| File | Responsibility |
| --- | --- |
| [traffic-rules.mjs](traffic-rules.mjs) | Named tuning constants, signals, right-of-way, gap predictions, following, and explanations |
| [driver-perception.mjs](driver-perception.mjs) | Visible lamps, per-driver observation memory, geometry-based intent inference, and cue evidence |
| [driver-behavior.mjs](driver-behavior.mjs) | Stable individual temperament and slider-dependent parameters |
| [junction-geometry.mjs](junction-geometry.mjs) | Shared routes, body poses, conflict zones, and clearance geometry |
| [junction.mjs](junction.mjs) | Arrivals, fixed-step integration, physical contact integration, and departures |
| [collisions.mjs](collisions.mjs) | Contact events, bounded roadside maneuver planning, hazards, and crash recovery |
| [chaos.mjs](chaos.mjs) | Each irrational action's eligibility, duration, and rule exception |
| [cop.mjs](cop.mjs) | Traffic Cop (COP-01): the round ladder, scripted rule breakers, admission classification, tickets and scoring |
| [horns.mjs](horns.mjs) | When drivers honk (HORN-01), who hears it, whom it points at, and the listener's reaction |
| [diagnostics.mjs](diagnostics.mjs) | Bounded events, snapshots, export schema, and diagnostic checks |

The IDs below are stable references used in downloaded logs. A decision's ID identifies its rule family; the accompanying reason and car ID explain the particular decision.

## Decision order

1. Generate seeded arrivals; admit offscreen queues only when the entrance has space.
2. Update signal detection, phase timers, and clearance.
3. Accumulate full-stop time. Each driver checks its own observed leading car, receiving space, inferred conflicts, and control rules. There is no global candidate sort or entry veto.
4. Drivers independently commit to movement. They remember observed entry/stop times; simultaneous hesitation uses visible right-of-way.
5. Compute following, turn speed, reaction delay, and interventions. Recheck sampled crossing motion; imminent collision predictions trigger bounded emergency braking.
6. Integrate normal cars on a shared physical substep clock, at most one quarter foot per substep. Contact stops the involved bodies at their last separated poses and records a crash, rather than silently withholding movement.
7. Recover crash-involved vehicles, update counts, remove departed cars, and record diagnostics. One step is 1/30 second.

## Rules and tuning

### RUN-01 — Arrivals and repeatability

Each street's selected hourly demand is divided between its two directions. Seeded arrivals have a 1.2-second minimum spacing plus an exponential random component. Driver choices and personalities use separate deterministic streams; Chaos Monkey has its own stream. Additional demand remains in offscreen queues when 120 active vehicles or a backed-up entrance prevents entry. A vehicle entering behind a queue arrives no faster than `entrySpeed` allows: the speed at which the MOVE-01 following model can settle behind the last car in that lane with ordinary braking, instead of arriving at full speed and braking hard. Cars placed at the start of a run begin no faster than they could stop for their line with ordinary braking, and no faster than they could settle behind the starting car ahead if it stopped. Arrivals must equal active + pending + departed vehicles.

Comparisons replay a seed across all five controls. A shared setup contains settings, not a replay of manual actions. Restarting or changing the layout/control starts a new diagnostic run.

### STOP-01 — Stops and queue order

`RULES.stop` requires one second below 0.5 feet/second within ten feet before the stop line. Each four-way-stop driver yields to a conflicting driver it observed stopping earlier; observation memory survives that vehicle starting to move. Departure hesitation is individual. There is no central stop-order sort. Opposing straight movements and other compatible movements may proceed together. Two-way stops apply the full stop only to Cross Street; Main Street retains priority. Each pocket has its own queue after the shared approach splits.

### YIELD-01 — Relevant vehicles only

`crossingWait`, `approachingConflict`, and `movementsConflict` check inferred swept paths and observed vehicle sizes. Drivers yield to conflicting committed vehicles, major-road traffic when entering from a minor road, and opposing through/right-turn vehicles when turning left. Compatible right/straight movements do not automatically block one another. A shared outgoing lane still has following/merge constraints. A car visibly held behind a stopped leader is not treated as freely approaching; this avoids opposing left-turners waiting forever for each other’s trapped followers. Conflict envelopes use dimensions rounded upward to the next foot in length and half foot in width for conservative cached geometry.

Gap acceptance is judged at the driver's own arrival: an approaching car's time to its line is compared with the threshold plus the time this driver still needs to reach its own line, relative to a driver already waiting there (never less). A rolling driver therefore decides early and steadily instead of discovering a "new" conflict in its last few feet. Where there is no left-turn bay, opposing left turns physically cross; the one that will reach its line first goes and the other eases to a stop, with the lower approach number breaking a tie.

**LANE-01 — Shared lane feeding turn pockets.** Cars bound for different pockets of one approach treat each other as the car ahead only while one of them is still in the shared part of the lane: until the two pocket centerlines are at least the two half-widths plus one foot apart (`approachSplit` and `inPathAhead` in `junction-geometry.mjs`). Earlier versions kept that relationship until both tails cleared the end of the taper, so a turner stopped up to 70 feet short of its line behind a car in the neighboring pocket.

### GAP-01 — Enter while the other car finishes

`crossingTiming` calculates each car's first and last overlapping body positions along the two routes. The waiting car may begin if its earliest arrival at the shared area is later than the other car's estimated clearance plus its caution buffer. It need not wait for the other car to leave the entire intersection.

- The other driver must already have reached the shared area, travel at least 8 MPH, show no observed braking cue, and not decelerate faster than 3 feet/second/second. Drivers do not read private Chaos commands.
- Estimate clearance at 85% of the other car's current speed, capped by its turn speed.
- Estimate the entering car's earliest arrival using the fastest personality acceleration multiplier, 1.55. Using this common conservative bound prevents a cautious car from receiving an artificially early release merely because it accelerates slowly. Rain reduces this acceleration bound and desired speed.
- Add `max(0.12, 1.5 − 1.25 × aggression)` seconds of clearance buffer: about 1.46 seconds at minimum individual aggression, 0.29 seconds at the old maximum, and 0.12 seconds at the new extreme maximum.
- After entry, `observedCrossingLeader` settles who goes first from what each driver can see, using one shared order: observed entry time, with a left turn or U-turn counted as entering half a second later (so it yields to oncoming traffic that entered with it), and the lower approach number winning entries within a quarter second. One order cannot loop. "Yield to the car on your right" was tried in this review and dropped: three or four drivers entering together each waited for the next one around.
- Two drivers stopped inside the intersection with conflicting paths settle a standoff: a body sitting in the other's path moves first, otherwise the one farther past its line, with the lower approach number breaking an exact tie. The outcome (`car.standoffs`) holds until the paths clear, so the winner pulling away does not reopen the question. A standoff winner edges past a stopped loser instead of freezing nose to nose; real contact becomes a crash.
- Two movements into the same receiving lane stop counting as crossing once either car is mostly (center past its exit) in that lane; the other follows it, measured along its own remaining path.
- Uses privately observed entry timing, not a shared priority number. If the prediction stops being valid, the driver brakes before the shared area. Emergency braking also projects sampled bodies onto their inferred paths; it cannot exceed the 28 feet/second/second braking limit. It counts only threats the driver's own motion closes (a car about to hit it where it already stands is no reason to brake), looks ahead at walking pace even when creeping so two slow cars cannot inch into each other, and expects a car that can still stop gently for its own stop sign or red light to stop at its line (`expectedToHalt`) rather than coast through at its current speed. A Chaos violator breaks that expectation, which is a real hazard.

An `admission` log event includes each still-conflicting car ID, arrival/clearance/buffer seconds, and conflict-zone front positions in feet. `uncertain-clearance` means prediction was disallowed, not a numerical ETA. `shared-path-clear` means the geometric conflict is already clear.

### EXIT-01 — Do not enter a blocked receiving lane

Check that the receiving road can fit the vehicle. Already moving departure traffic is handled through following distances. Do not reserve an entire junction solely because a departing car remains visible.

### SIGNAL-01 — Signals

Timed lights split a 50-second green budget between the two streets. Presence lights use 85-foot detection zones, configured minimum/maximum green, and a three-second gap-out when the opposing street has demand. With no opposing call, a presence green may rest. Both use three-second yellow and two-second all-red clearance. A light does not secretly wait for every car to clear; drivers must notice remaining traffic themselves. Already admitted cars finish through yellow/red. Yellow decisions follow SIGNAL-02. The manual all-red hold prevents new normal entries until signals resume.

A driver already waiting at the line when its light turns green reacts to the change: its `reaction` hesitation counts from the green, not from when it first stopped, so it no longer pulls away in the same 1/30-second frame. A driver that stopped less than a second before the green (and so has no full-stop record yet) reacts the same way.

Right turn on red is not modeled. Protected left-turn phases are optional (SIGNAL-03). These are model choices, not claims about every U.S. intersection.

### SIGNAL-02 — Yellow decisions

A yellow is a warning, not an instant red. `yellowChoice` makes each driver decide once, when it first sees its own yellow, and remembers that choice for the rest of the change:

- **Stop** if braking to two feet before the line needs no more than the driver's `yellowStop` (8 + 6 × (1 − aggression) feet/second/second, about 5.9 for the extreme driver, 8.2 at the previous maximum, and 13.8 for the most cautious). Normal following brings it to the line.
- **Continue** if stopping would need harder braking and the driver expects to reach the line in time. "In time" is personal: drivers up to 50% aggression want to beat the red (current speed × yellow time); above that a driver accepts entering up to `2 × (aggression − 0.5)` seconds after the red, about 1.7 seconds for the most extreme driver. Otherwise it brakes harder than it would like and stops. A driver that chose to go follows through within the lateness it accepts, or when even 28 feet/second/second emergency braking can no longer stop it; otherwise it stops. Running a red this way is driver behavior, not a model error: the log marks it `ran-red-light` for review.
- **Finish a waiting left turn.** A left-turner already stopped at the line when the yellow appears may turn during the yellow. It still yields to committed traffic and to opposing drivers who look unable to stop. An opposing driver who can stop with 8 feet/second/second or less (gentler than every personality's threshold) is expected to stop. This gives a permissive left turn roughly one chance per cycle instead of waiting through several greens. Left-turners still wait behind the line rather than pulling into the intersection, and a left-turner that arrives during the yellow stops.

The three-second yellow is short for a 45 MPH limit (common U.S. practice is about 4.3 seconds). Ordinary drivers now account for it by stopping when the line is out of reach; aggressive drivers may run the red as described above.

### SIGNAL-03 — Protected arrow, then permissive green

`leftSignal` offers `permissive` (circular green, left turns yield) and `protected-permissive` (a leading left arrow followed by circular green). The arrow option requires a dedicated left pocket: selecting it in the UI adds one if necessary; removing the pocket returns to permissive control. The option is saved/shared and participates in comparison keys. Stop-sign controls and the roundabout ignore this signal preference.

Each street's protected phase gives both opposing left turns an eight-second green arrow; all through/right movements and the other street remain red. It ends with a three-second yellow arrow and two-second all-red interval, then the same street gets its usual circular-green phase. Circular green permits left turns only after yielding to opposing through/right traffic. That phase ends with circular yellow and all-red before the other street begins. The five-section shared signal face displays circular red/yellow/green plus separate left yellow/green arrows. All-lights-red completes the current yellow and holds all movements red.

Timed control always includes the arrow. Detected control includes it only when a left-turn vehicle is within the 85-foot detection area at phase selection (including startup); a same-street left-turn call can also end circular green after its normal minimum/maximum/gap rules so it cannot be ignored indefinitely. A left call arriving after phase selection waits for a gap or the next cycle. Arrow duration is eight seconds; the green-balance and min/max controls apply to circular green. This simplified controller does not implement every U.S. phasing arrangement or right-on-red.

`signalColor(state, lane, turn)` returns the indication for that movement; omitting `turn` gets the through indication. Signal events include `movement` (`left` or `through`); admissions include `signalMovement` and `protectedLeft`. A protected arrow removes the normal opposing-approach yield requirement, but drivers still observe vehicles already crossing, receiving space, and emergency threats. A stopped left-turner does not start on a yellow arrow; circular-yellow completion retains SIGNAL-02. No central entry reservation is added.

References: [FHWA signal timing manual, left-turn operation](https://ops.fhwa.dot.gov/publications/fhwahop08024/chapter4.htm) and [TxDOT protected/permissive shared versus separate signal faces](https://www.txdot.gov/manuals/trf/tff/traffic_signal_projects/flashing_yellow-gjfldobj/modes_of_operation-i1044992.html). Shared circular green conveys permissive operation; a separate permissive left face would need a flashing yellow arrow instead. The sandbox uses the shared face.

### RING-01 — Roundabout

A single counterclockwise circulating lane has entry-yield and front/rear gap checks. Following measures along the driver's own path: before reaching the circle, a driver measures from its merge point, so a circulating car that has not yet passed that point is crossing traffic for the entry yield, not a car "ahead" with a negative gap. Earlier versions projected the approaching car backward onto the circle, which made entering cars stop dead about 20 feet before the yield line and blame the wrong car. Drivers follow the shared ring and leave at their selected exit. Turn-lane preferences are retained for other controls but do not widen the circle. A passing vehicle releases the entry after its tail plus the five-foot standstill clearance; normal following still controls space ahead. The old additional 18-foot yield margin unnecessarily held cars after the entry cleared. Approaching vehicles retain the personality-dependent rear gap. Entry gap varies with personality and is judged at the driver's own arrival at its merge point (YIELD-01). When a car on the circle, or one close to its yield line on another arm, could still close the gap, an entering driver approaches ready to stop at the yield line ("Slowing to check the circle.") instead of discovering the conflict in its last 20 feet. With the circle clear it enters at the normal 15 MPH. Physical contact checks include curved trucks and their tails.

### MOVE-01 — Following and speed

Following uses an Intelligent Driver Model-style acceleration calculation with a five-foot standstill gap and personality-dependent time headway. Base acceleration is 8.5 feet/second/second for cars and 5.6 for trucks, multiplied by personality and weather. The maximum emergency deceleration clamp is 28 feet/second/second. These are tuning values, not calibrated driver measurements.

A stop line uses its own braking target instead of a stopped virtual leader in the following calculation. Drivers accelerate into available space, then brake toward 5.1 feet before the line using a discrete-step stopping-speed envelope at 9 feet/second/second (7.38 in rain). The leader-following limit still applies, and final acceleration is physically bounded; there is no position clamp. This avoids applying time headway to a fixed line and creeping asymptotically toward it. Logs expose `stopLineDistanceFeet` and `stopTargetSpeedMPH`; these are null when no line stop is requested. Thoughts describe approaching a stop until the driver commits, and distinguish circular-green yielding from protected-arrow turns.

Normal turn targets are 12 MPH right, 16 MPH left, and 15 MPH circulating; Chaos U-turns target 8 MPH. A turning driver slows along a 9 feet/second/second (7.38 in rain) braking curve that reaches the turn speed where the turn begins; earlier versions switched the target 65 feet out and braked at the 28 feet/second/second limit. Turn speed limits release when the vehicle center reaches the exit. A driver found above its stop or turn curve (a late decision to yield) brakes at the steady rate the remaining room needs instead of snapping onto the curve, and within half a foot of its stop target it stops outright rather than creeping over the line. Approach speed is capped at the selected speed, individual variation, and weather adjustment. The headway, acceleration multiplier, reaction delay, speed multiplier, and accepted gaps are centralized in `driverProfile`.

### BODY-01 — Physical contact, not invisible intervention

The renderer and contact integration use the same oriented dimensions and poses in feet. The physical layer checks simultaneous proposed poses and each body's previous pose, stopping the involved bodies at the last separated positions. Sampling is at most 0.25 feet; there is no old 0.6-foot preventive clearance buffer. Vehicles cannot pass through one another. Normal movement has no exact-position following clamp or stop-line clamp; braking is bounded and can be too late. Contact resolution is simplified (no impact forces, deformation, injuries, or rebound).

### CRASH-01 — Consequences and road clearance

Crashes arise from actual contact, never a random crash timer or quota. Ordinary driving should usually avoid them through perception, gap judgment, and braking. Aggressive driving or Chaos can make mistakes consequential. Tests separately exercise ordinary traffic and deliberately unavoidable impacts.

On contact, both drivers stop, cancel any Chaos maneuver, and switch on visible hazard lamps. After 1.5 seconds they independently search short forward/reverse steering arcs around nearby bodies toward the roadside. Maneuvers are capped at 4 MPH, with a 19-foot turning radius for passenger cars and 28 feet for trucks. Planning has a bounded node budget and retries when blocked; each small movement checks occupied space and waits/replans instead of teleporting. The roundabout center remains excluded. Decorative plants and street furniture are not solid obstacles in this simplified recovery model.

A vehicle is clear only when its entire oriented body is outside a conservative road/curb envelope. It parks with hazards for six simulated seconds, then leaves the scene. All crashes are modeled as drivable pull-over incidents, regardless of impact speed. This is an explicit sandbox simplification. A blocked maneuver may take longer; there is no timeout that removes an on-road car.

`collision` records both participants' pre-impact speed, pose, individual aggression, reason, motion settings, perception, and Chaos state, plus relative closing speed in MPH. `recovery-start` and `road-cleared` record the recovery lifecycle and duration. Body penetration remains an error independent of an intentional crash event. Crashes count once per new contact involving a previously uncrashed car; repeated contact between two already crashed cars is not counted again. A pileup may involve multiple contact events.

Recovered cars count as departures. They do not count as completed crossings unless their rear had already cleared the intersection before impact. `crashDepartures` tracks recovered departures without a completed crossing, preserving arrival conservation and honest comparisons.

### INPUT-01 — Manual pedals

Gas (`G`) pins the selected accelerator for four seconds at 1.7 times that driver’s acceleration, capped at 45 MPH. It overrides signals, stop requirements, following, turn-speed limits, and emergency braking for that driver only. Other drivers still react from their observations; no crash is scripted. `manual-gas` records the command and motion/snapshots retain the remaining duration. Brake cancels Gas; Gas cancels Brake. Impact cancels both. Pedals cannot affect crashed cars and are disabled during automatic comparisons.

Tapping the brake command (`B`) applies braking to the selected car for three simulated seconds. Other traffic continues to follow/yield, and collision checks remain active. Pause and About stop simulation time, including intervention timers.

### CHAOS-01 — Explicit irrational exceptions

Only one event may be active. Choose among eligible visible drivers using a separate random stream, preferring moving crossing traffic when that action permits it. The action bag avoids repeats among available choices; unavailable actions do not prevent another action.

| Action | Motion and exception | Ends |
| --- | --- | --- |
| Freeze | Unexpected braking to a stop | 9 simulated seconds |
| Crawl | Target speed capped at 3 MPH | 15 simulated seconds |
| Yo-yo | Alternate 2 seconds of normal driving with 2 seconds of panic braking, starting with driving | 12 simulated seconds |
| U-turn | Replace the planned route before entry with a continuous return to the same road; may cross lane-use restrictions | When the rear clears the maneuver |
| Violation | Skip the required stop or enter against a red signal | 20 simulated seconds |

U-turns are passenger-car-only and start at least eight feet before entry; trucks and already committed cars are ineligible. U-turns and violations are unavailable in the roundabout and cannot select a driver trapped behind another car in the same incoming lane. Violations still use the driver’s observed conflicts and exit space, but skip the traffic control. Their imperfect decisions can now cause contact. U-turns keep their changed route after the intervention ends. Chaos is disabled during automatic comparisons and counted in manual experiment results.

### MIND-01 — Judgment, attention, and habit

Drivers differ beyond aggression, and the same driver differs from moment to moment. Every seeded car carries three stable traits from `driverTraits(seed, id)`: `temperament` (aggression, unchanged), `attention` (0–1) and `judgment` (−1 … +1; positive is an optimist, negative a pessimist). They are independent, so a bold-but-sharp driver and a timid-but-inattentive one both exist. A car without traits (test fixtures) is the old perfect driver: full attention, neutral judgment, nothing random.

- **Judgment error.** A driver's estimate of another car's speed is off by its personal bias (`judgment × 12%`) plus a per-pair scatter of up to `16% × (1 − attention)`, fixed for that pair of cars for the whole encounter (`judgmentError`). An optimist reads oncoming traffic as slower and farther from its line than it is; the distance error is a fraction of the separation beyond 30 feet and applies to traffic in other lanes only. A car in the driver's own lane is placed where it is (whether a queue tail blocks the lane is plain to see; misplacing it by 20 feet made drivers accelerate into stopped queues), though its speed is still misjudged. Gap acceptance, crossing timing, roundabout entry, and the expected-to-halt check all run on these beliefs; `expectedToHalt` therefore trusts a car visibly braking toward its line to stop with up to half again its current rate, instead of reading a pessimist's inflated speed as a runner.
- **Decision wobble.** Every accepted gap (`mainGap`, `leftGap`, `ringGap`), the crossing buffer (half weight) and the yellow comfort limit (double weight) move by a seeded value of up to `±0.6 s × (1 − attention)`, redrawn every two seconds (`decisionWobble`). The same driver sometimes goes and sometimes waits on the same gap; waiting longer sees more draws, which is impatience.
- **Lapses.** On each observation scan an inattentive driver may look away with chance `0.004 × (1 − attention)²` (three times that while stopped) for `1–2.5 s × (0.6–1.4)`. During a lapse it does not scan: every car it last saw is carried forward at the speed it last saw (`seenX + v × elapsed`), a car that braked or arrived meanwhile goes unnoticed, and a driver stopped at the line pulls away only when the lapse ends. The thought reads "Not looking for a moment." / "My turn, but I am not looking yet." and the log records a `lapse` event. On looking again the driver reacts with whatever braking is left, up to the emergency limit; a close follower can hit the car ahead.
- **Rolling stops.** A driver with `aggression > 0.55` and the habit (seeded, likelier the more casual its attention) counts a crawl below `3 + 3 × aggression` mph held for 0.3 s inside the line tolerance zone as its stop (STOP-01 otherwise requires 1 s at rest). It reaches that crawl six feet before the zone and holds it; after its stop counts it still yields like everyone else and stops at the line when it must. Other drivers never see it `stoppedAt`, so the four-way-stop order does not favor it, and they expect it to halt, so its entry can surprise them. The admission carries `rolledStop` and the check is a `rolling-stop` review flag, not a `missed-stop` error; right on red after a rolled stop is the same flag.
- **Roundabout standoffs.** A misjudged entry can leave an entering car nose to nose with a circulating one. The GAP-01 standoff rule now settles those too, and a circulating driver treats a stopped car whose tail is still in the entry throat as yielding and edges past it; a nose poking into the circle gets clipped rather than freezing the circle.
- Crash records show what each driver believed about the car it hit (`perception.other`: observed and actual speed, judgment error, lapse state). Same seed, same drivers, same mistakes.

### HORN-01 — Honking and its result

A horn is a public cue, like a blinker: drivers honk from their own observations (judgment errors included), and every driver within 220 feet hears it. Who a honk was meant for is private. Listeners work out whom it points at from what anyone can see, so the wrong car sometimes reacts. Tuning lives in `HORN_RULES` (`horns.mjs`); per-driver habits come from `driverProfile`. A car without traits (test fixtures) never honks.

**When a driver honks**

- **Impatient (stalled leader).** The driver is stopped within 35 feet behind the car directly ahead in its lane, and that car sits at its line (or crawls under 5 mph on open road with nothing in front of it), the honker is not looking away, and the honker sees no reason to wait: its own light is green for the leader's inferred movement; no car is in the intersection across the leader's path; at a four-way stop no conflicting car stopped first; no receiving-lane backup; and no conflicting car (not expected to halt) reaches its line within `1.5 + 3 × (1 − aggression)` seconds. The clock starts when the road looks clear, and not before the leader's own full stop has counted at stop signs. The driver honks once its patience runs out: `max(1, 1.5 + 5.5 × (1 − aggression))` seconds, 1.5 times that for a crawler, scaled by a seeded 0.8–1.2 per leader. Later honks at the same leader come after `patience × (1 + 0.7 × honks)` and at least 2.5 s apart, are long, and stop after three. Whether a driver honks at all is a seeded habit, likelier the bolder it is (`15% + 75% × aggression`, capped at 95%).
- **Warning (forced to brake).** A moving driver (over 6 mph) emergency-brakes for a car, or brakes harder than 12 ft/s² for a car crossing its path, a car that has pulled out in front of it, or (above 12 mph) a car merging in ahead, while it had the right of way (already in the intersection or circle, on the main road and not turning left at a two-way stop, or on a green that is not a yielding left). Never at a car ahead in its own lane or behind it, and never at a car whose real movement neither crosses nor merges with its own (`movementsConflict` on the actual routes, or the same receiving lane): the driver may have braked on a wrong guess about where the other car was going, but it does not honk at a car that was never in its way (2026-10-30: a right-turner had honked at a left-turner it took for a through car). After a reflex of `0.15 + 0.25 × (1 − aggression)` s it honks, long above 18 mph or 80% aggression; at most once per car every 4 s. Most drivers have this habit (`50% + 45% × aggression`, capped at 97%). The same test, honk or not, logs a `forced-brake` event (braker, cause, braking rate, speed) at the start of each episode, except within 1.5 s of the braker looking up from a lapse; COP-01 reads it as the cause's failure to yield.

**Whom it points at.** A stopped honker's horn points at the nearest car within 0.7 radians of straight ahead and 150 feet. A moving honker's points at the car on course to come within 14 feet of it in the next 3 seconds, judged from visible positions, headings and speeds; a car pulling away or passing clear does not take it personally.

**Result**

- Every driver within earshot that had looked away (MIND-01 lapse) looks again at once and scans.
- A honk from a stopped car behind is a **nudge**: if the addressed driver is still at or before its line, it takes `0.4 + 0.8 × (1 − aggression)` seconds off its accepted gaps (main road, left turn, roundabout) for 6 s and halves its start-up reaction on a green. A timid driver feels it most. It can push a driver into a gap it would have refused, and a crash remains a natural consequence.
- A honk from a moving car is a **warning**: after `0.2 + 0.5 × reaction` s, an addressed driver short of its line brakes at 16 ft/s² for 0.9 s (stops short); one already past its line and not braking for anything accelerates out of the way for 1.5 s. A driver already slowing for a car (a crossing car, a circulating car at its merge) keeps doing so: hurrying into it put an entering car nose to nose with a stopped circulating car and locked the circle. Emergency braking still overrides.
- Thoughts: "Honking at car N: the light is green." / "…: it is in my way." on the honker for the honk plus one second; "Car N honked at me." on the nudged driver for 2.5 s; "Car N honked at me. Stopping short." / "Hurrying out of its way." while startled.
- The map draws sound arcs from the honking car's front bumper; with sound on, the horn plays panned to the car (long honks hold, trucks sound lower).

## Individual perception and execution

`driver-perception.mjs` gives each car its own observation memory. It scans nearby vehicles within 260 feet at a personality-dependent interval (roughly 0.15–0.37 seconds). Cars do not share a scan clock or memories. The geometric projection cache only avoids repeating measurements of the same public road; it shares no beliefs or decisions.

- The renderer and observers use the same `vehicleCues` lamp states. A driver remembers an observed turn signal for 1.2 seconds so a dark flash interval does not erase it.
- A dark signal that has never been seen supplies no intention. Drivers initially favor straight travel, then combine observed blinkers with lane position and heading to infer a route from public road geometry. A developing U-turn is recognized from heading and position, not from the private Chaos command.
- Visible U-turn recognition: once a vehicle turns more than 0.7 radians from its approach heading, observers also compare public U-turn paths against its position and heading. This prevents a curling vehicle from being mistaken for a straight-going car until it faces backward; observers still cannot read its private route or Chaos command.
- Brake lights are readable from behind a vehicle, not from in front. When a following driver sees them, it adds its own caution margin to following time before the leading car's speed necessarily falls. New information waits for that driver's next observation.
- Turn-signal confidence varies with personality and affects approach-gap caution. Observed deceleration comes from successive speed samples. Estimated turns, visible lamps, and sampled motion are inputs to following, exit-space checks, circulating-traffic checks, and approach gap decisions.
- Each car independently requests entry or yields. There is no group throughput objective or turn-taking courtesy policy. Right-of-way rules still apply.

The **physical layer** integrates movement and resolves contact only; it does not order entry requests, reserve actual routes, or veto a perceived safe gap. `motion.observedCrossingCarId` and `motion.emergencyBrakeCarId` identify observation-based braking. Legacy `executionHoldCarId` / `clearanceGuardCarId` fields are removed in schema 4. Hazards are visible cues, and parked/off-route bodies do not remain phantom lane queues. Recovery maneuvers have their own local obstacle planning and physical contact checks.

This remains an illustrative model: building/vehicle occlusion is not modeled, observed position/speed measurements carry only the driver's own bias and per-pair scatter (MIND-01) rather than a calibrated vision model, and drivers follow predetermined road paths except during crash recovery. Independent decision-making does not imply a calibrated human-vision model.

## Aggression audit

`driverProfile` is the shared source for individual aggression. The population slider ranges from 0 to 150%. Settings through 100% retain the original individual range of 3–97%. Above 100%, the individual cap rises gradually to 135%; the mix and stable temperament still distinguish drivers. These percentages express aggression relative to the original scale, not a probability. Check the actual individual level at the event time when evaluating hesitation.

| Decision | Personality parameter used |
| --- | --- |
| Following | `headway`, with a rain multiplier |
| Pulling away | `acceleration`, applied to the vehicle's base acceleration and weather |
| Departure hesitation | `reaction` |
| Desired speed | `speed`, applied to selected speed, vehicle variation, and weather |
| Minor-road gap acceptance | `mainGap` |
| Left turn across opposing traffic | `leftGap` |
| Roundabout entry | `ringGap` |
| Stop or continue at a yellow | `yellowStop` |
| Entry behind a clearing vehicle | `crossingBuffer` |

The individual cap is `0.97 + max(0, mix − 100) × 0.0076`; effective aggression is `max(0.03, min(cap, 0.15 + mix × 0.007 + (temperament − 0.5) × 0.8))`. At the extreme maximum, following headway bottoms out at 0.55 seconds, crossing clearance at 0.12 seconds, and reaction multiplier at 0.12. Signal trust caps at 0.98 and desired-speed multiplier at 1.0. Other personality parameters retain their formulas: at 135% aggression, main-road/left-turn/roundabout gaps are 2.83 / 2.405 / 1.415 seconds and acceleration is 1.7775 times the vehicle/weather base. Physical braking limits, full stops, and turn speed limits are unchanged.

The expanded range advances logs/comparisons to junction-v15. `tools/traffic-extreme-test.mjs` verifies an actual earlier entry than the previous maximum, stronger departure acceleration, expanded diagnostic evidence, personality variation, and extreme traffic/drainage under all controllers in dry and wet conditions.

Full-stop compliance, signal indications, turn speed caps, and bounded braking remain rules at every aggression setting. Chaos explicitly overrides only its documented rules. The 2026-10-25 review fixes ([REVIEW-NOTES.md](REVIEW-NOTES.md)) advanced the driving model to junction-v12. The queue and protected-arrow update advances it to junction-v13; older comparison results remain separate.

## Diagnostic log

`CHECK-01` identifies evaluation flags in the event stream.

Schema 4 adds crash/recovery counters and events, snapshot crash phases, hazard observations, and pre-impact participant evidence. Crashes appear in comparisons and the map footer. No crash rate is injected. Schema 3 added `perception` to car events and snapshots: individual scan times, entry intent, observed-car count, the relevant blocking car's cues, and up to six nearby observations. Each observation distinguishes visible indicator/brake lights (null means not observed/visible), inferred turn, information source, confidence, observed speed, and observation time. Driver context includes observation interval, signal trust, brake-cue caution, and yellow-stop deceleration. Current movement decisions no longer have central safety holds. Geometry caches and other drivers' private profiles are not part of the information supplied to its mind; the analyst-only driver/blocker contexts remain available for evaluation.

Open **About → Simulation log → Download log** before resetting a run. Nothing is uploaded. Schema 2 added `driverContext` to every car-specific event, snapshot, and diagnostic flag. It includes the observed time, mix percentage, stable temperament, individual aggression percentage, label, and named thresholds with seconds/multiplier units. Blocking drivers and conflicting reservations carry their own context. Departure events retain the last observed profile. Earlier events keep their original values after slider changes. A changed slider or weather setting refreshes the car's decision on the next simulation step even if its reason is unchanged.

Decision events and snapshots also carry `motion`: the evaluation time and aggression used, actual evaluated headway, desired/target speed in MPH, acceleration limit in feet/second/second, reaction delay, and weather for that movement step. Initial snapshots have no movement evaluation yet, so `motion` is null. These limits are planning inputs, not a claim that a waiting car is accelerating or moving at its target speed.

When assessing a report, use the individual driver and blocker profiles at the decision time, then compare the applied threshold to the logged timing evidence. Account for fixed right-of-way and collision constraints as well as aggression.

The JSON includes schema/model versions, seed, initial/current settings, units, counters, current vehicles, and bounded history:

- Last 3,000 events: driver visibility, changed decisions and blocking IDs, admissions with timing evidence, signal changes, settings changes, interventions, departures, and checks.
- Last 120 position snapshots, sampled once per simulated second, with poses, dimensions, speeds, acceleration, personalities, stop/admission state, signal, and pending counts.
- Last 100 diagnostic flags; dropped-entry counters show truncation. Event counts and overlap totals cover the entire run.
- Admissions on a non-green light carry `signalDecision` (SIGNAL-02: `go`, `clearingLeft`, `expectsRed`, needed and accepted deceleration, `cannotStop`, or `ranRed` with `secondsIntoRed`). A yellow entry with a `go` or `clearingLeft` decision is legal. A red entry by a driver who chose to continue and could no longer stop is an `entered-on-red-after-yellow` review flag; a red entry an aggressive driver chose is a `ran-red-light` review flag; any other non-green entry is a `signal-violation` error.
- MIND-01 adds `lapse` events (driver, length, speed, whether already committed), `rolledStop` and `lapsing` on admissions, `lapsing` / `gapWobbleSeconds` / `rollingStopMPH` in `motion`, attention, judgment, bias, wobble, lapse chance and rolling habit in every `driverContext`, and `actualSpeedMPH` / `judgmentErrorPercent` beside each observation's believed speed. A stop-sign or right-on-red entry on a rolled stop is a `rolling-stop` review flag. Collision participants carry `perception.other`, the hitter's belief about the car it hit.
- HORN-01 adds `honk` events (honker, `kind` impatient or warning, `long`, `targetCarId` the honker meant, `addressedCarId` the car the horn points at, `waitedSeconds`, speed, `heardBy`, and `lookedUp` listeners whose lapse it ended) and `honk-reaction` events (addressed car, `fromCarId`, `response` pressured / stop-short / hurry / heard, `lookedUp`, `pressureSeconds`). Horn habits, patience and pressure are in every `driverContext`; `motion` carries `leaderCarId`, `yieldCarId`, `hornPressureSeconds`, and `hornStartle` with its car. The summary counts `honks`.
- Checks flag nonfinite motion, sampled body overlaps, broken arrival conservation, ordinary admissions without the required stop/green; uncertain inferred admission clearance is a review hint, and a committed car stopped for 20 seconds. A long stop is a review hint, not proof of deadlock. One-second overlap sampling does not prove every intermediate step was collision-free; automated stress tests check every step with an independent polygon test.

To investigate hesitation, find the car's decision and blocker, then its next `admission`. Compare arrival time against clearance + buffer and inspect nearby position snapshots. For a possible jam, inspect long-stop flags and reciprocal blocking IDs. For Chaos, correlate `chaos-start`, the changed route or `illegal` admission, neighboring decisions, and `chaos-end`. The bounded log is not a complete deterministic input replay.

## Change and validation checklist

1. Identify the product requirement and rule ID, then edit the named function/constants instead of adding an unrelated special case.
2. Add a regression reproducing the behavior, including movement and recovery assertions. Preserve independent every-step overlap checks.
3. Run `node tools/traffic-sandbox-test.mjs`, all three traffic browser suites, `node tools/game-procedure-check.mjs traffic-sandbox`, and `node tools/es2019-check.mjs` (0 syntax / 0 runtime gaps).
4. Check straight-path alignment, normal truck and U-turn passenger-car pavement clearance when routes change. Check About focus, Escape, pause preservation, and log download on desktop/mobile.
5. Update these rules, the product reference when requirements change, README, and the dated release history. Change the model/comparison version for behavior changes and refresh module/cache tokens.

`tools/traffic-perception-test.mjs` verifies actual blinker-dependent entry decisions, hidden-plan independence, signal memory, independent scan times, brake-light visibility and reaction, cue evidence, and physically bounded stopping. `tools/traffic-collision-test.mjs` verifies forced crashes, continuous recovery, hazard lamps, off-road clearance, accurate counters, and ordinary-flow crash frequency.

`tools/traffic-rules-test.mjs` specifically covers anticipated left-turn clearance at all three lane layouts, caution differences, surprise braking after admission, all U-turn origins, and actual red-light/stop violations. `tools/traffic-diagnostics-test.mjs` checks log evidence and bounds. `tools/traffic-junction-test.mjs` covers normal flow, controller rules, shared paths, queue drainage, and repeated Chaos interventions.

`tools/traffic-review-fixes-test.mjs` covers the 2026-10-25 review fixes: roundabout entries yield rather than following circulating cars upstream of the merge point, yellow go/stop and left-on-yellow decisions with their log evidence, green start-up reaction, in-junction standoffs, yo-yo order, and arrivals settling behind a stopped queue in clear and rain. Each block fails on the junction-v11 code.

`tools/traffic-queue-signal-test.mjs` covers finite queue move-up in clear/rain and all aggression settings, the reported seed-1 four-stop run, leader braking, truthful approach thoughts, arrow/circular-green right-of-way, clearance and hold sequencing, detector calls, diagnostic evidence, and six three-minute ordinary arrow runs. These queue/signal changes advance comparisons and logs to junction-v13.

### COP-01 — Traffic Cop

The game mode (`cop.mjs`, cop-v3). The player clicks every driver who breaks a rule; the sandbox is untouched. Rounds climb a ladder (`copRound`): two-way stop (stop runners, speeders, tailgaters), busier two-way stop (+ blinkers), four-way stop, timed lights, lights with a left-turn lane, detected lights with two turn lanes, roundabout; beyond that the busiest layouts repeat on laps with more traffic, bolder drivers and a bigger quota (capped at 10 breakers and 130% aggression), and each lap shortens the ticket window by a second (9 s down to `minWindowSeconds`, 4) and packs scripted breakers closer (7 s down to `minScriptGapSeconds`, 3): `roundWindow` / `roundScriptGap`. There is no last round. Each round is a fresh model from `copSettings` (seed from the run seed and the round, mistakes on, 10% trucks, 35 mph).

- **Quota.** Each round has a quota of rule breakers. A seeded scheduler scripts one at a time (`car.scripted`), spaced about 7 s apart, only while the round still needs them, taking the round's kinds in turn (every round lists at least two) and waiting up to 4 s for the kind whose turn it is before trying the others: `run-stop` / `run-red` make `ignoresControl` true so the driver skips its stop or red (chosen from visible approaching drivers with a clear lane; a red runner is chosen only on a non-green), `no-signal` sets `forgetSignal`, `wrong-lane` swaps in `wrongLaneRoute` before the pocket split (upstream of the view), `u-turn` swaps in `uTurnRoute`, `barge` makes a roundabout entry ignore the circle (`crossingWait` returns null; the admission records `barged` when `circleConflict` was real), `speed` gives a through driver on open road a target of 1.5× the posted speed (`RULES.script.speedingFactor`; set to it at once while still out of view, so it arrives fast), and `tailgate` picks a moving car on a priority approach or a holding green and puts a follower on its bumper out of view (the next car in the lane moved up, or a new arrival with the leader's route, counted in `arrivals`) at the leader's speed with a quarter-second headway and no reaction room (`RULES.script.tailgateHeadwaySeconds`; emergency braking still applies). Scripts clear once the driver has passed the exit; the sandbox never scripts anyone.
- **Ground truth.** Every admission event is classified by `violationOf`: U-turn; a required stop (four-way, minor road, right on red) under 1 s is `ran-stop` or, for a habitual roller, `rolling-stop` (`rolling-right` on a red), unless the car crept through its line under `creepMPH` (4 mph; the admission carries `slowestMPH`, the slowest it got in the 10-foot stop zone, or its speed at commitment if it never slowed): a near-stop is not a roll anyone could call, so it is no breaker, and a scripted runner held up by crossing traffic can be pardoned this way (the scheduler then scripts another). The same pardon goes to a car that was held under a walking pace (`RULES.stop.queueCrawlMPH`, 4) for a second (`heldSeconds`, from the admission's `heldSeconds`) within a queue slot of its line (`RULES.stop.queueFeet`, 40), typically queued behind the car at the sign: everyone watching saw it stop, even if it then only crawled over the line itself (2026-10-31: Trevor saw a habitual roller that had waited twenty seconds in line ticketed for rolling the stop at 8 mph). Stop tickets name the speed ("ran the stop sign at 24 mph", "rolled the stop sign at 5 mph") and the breaker event carries it; an illegal or chosen red entry is `ran-red` (a yellow `go` / `clearingLeft` and a `cannotStop` red are legal); then a roundabout barge (`failed-to-yield`), `wrong-lane`, `no-signal`. One violation per driver, scripted or natural, so the drivers' own mistakes (MIND-01) count. The log carries `cop-round-start`, `cop-script`, `cop-breaker`, `cop-ticket`, `cop-pending-ticket`, `cop-wrong-ticket`, `cop-missed` and `cop-round-end` events.
- **Speeding and tailgating (cop-v2).** Read from motion each step, not from admissions: a driver in view at or above 1.3× the posted speed is `speeding` ("was doing 52 in a 35"); one within a 5-foot standstill plus 0.5 s of travel of the car ahead on its path, at 20+ mph, for 1.5 s in view, is `tailgating` ("was tailgating car 7"; the breaker event carries the leader, gap and headway). Natural traffic never trips either: habits top out at 1.07× the limit and ordinary following settles beyond a second of headway (0.77 s at the Extreme mix).
- **Failure to yield (cop-v2, widened 2026-11-01).** A clean admission on a movement that had to give way (`hadToYield`: the minor road or a left at a two-way stop, anything at a four-way stop or roundabout, a non-green or a permissive left at lights) goes on watch (`cop.yielders`) for as long as the car is in the crossing (`yieldMaxSeconds`, 25) and `yieldSeconds` (6) after it leaves (`yieldOpen`), so a left-turner that waits in the middle for an opposing car and then finishes in front of someone is still answerable. Two things make it a `failed-to-yield` breaker while on watch and past its entry: a driver with the right of way logs a `forced-brake` for it (HORN-01: hard braking, honk or not; `why: 'forced-brake'`), or, `madeToWait`, a driver with the right of way (`hadTheWay`) whose motion names it (emergency brake, observed crossing car, or a committed yield car, the same attribution as the horn) and whose real path meets it slows by `slowedMPH` (15) or down to `waitMPH` (10), from at least 20 mph (`why: 'made-to-wait'`, `slowedFromMPH`/`slowedToMPH`). Either way the blamed car must be moving (`movingMPH`, 6; `causeMPH` on the event) and still in the crossing (no more than 8 ft past its exit) at that moment: a driver braking for a car sitting still in the middle, a left-turner waiting for its gap, braked on its own caution, and that car never turned in front of anyone (Trevor, 2026-11-01: "said a guy turned in front of oncoming traffic, but he didn't really"). Named for what it did: "pulled out in front of a car", "turned in front of oncoming traffic", or "cut off a car in the circle"; the cop-breaker event carries `forcedCarId`. A click on a car on watch in the crossing is held like a click before the line. (Trevor, 2026-11-01: a minor-road left-turner at a two-way stop waited mid-crossing for the opposing car, then finished in front of a main-road car that had to slow, and was called clean: the slowdown was under the horn's hard-braking rate and past the old 6-second window.)
- **Tickets and the shift.** A breaker stays ticketable until it drives off: a car past its exit is missed once it is 0.5 s of travel (`missLeadSeconds`, never less than 12 feet) from the edge of the map on its heading, or out of view, so the miss is called just as the car can be seen leaving, never while it is still in view (`cop-missed` carries `why: 'left'`). The round's window (9 s on the ladder, `windowSeconds`) is only a backstop for a breaker that can never leave: wrecked, missed after the window (`'wrecked'`), or stuck anywhere for `stuckSeconds` (60, `'stuck'`). A click on a car already called as a miss is `late` and costs nothing. (Before 2026-10-30 the window itself ended a ticket, which called slow or queued breakers missed while they were still plainly on the map.) A click on it scores 100, 150 within 2.5 s. A click on a moving car still short of its entry is held for 1.5 s (`cop-pending-ticket`): a violation in that time is a quick ticket, otherwise it is a wrong ticket. A click on any other car costs 50 points at once (`cop.wrongTickets` counts them; they never end the shift). Every miss adds to `cop.streak` and a ticket resets it; at `missStreak` (3) misses in a row the shift is over (`overReason: 'streak'`), mid-round if need be, and the partial round goes on the books with `complete: false`. A round completes when caught + missed reaches the quota; catching every breaker with no wrong ticket adds a 200 bonus. Score never drops below zero. (cop-v2 ended the shift on three wrong tickets or a round with fewer than half caught; Trevor wanted endless rounds that only get harder, ended by three misses in a row, 2026-10-28.)
- **Rolling stops on duty.** `copSettings` sets `rollingMPH: 10`, a floor on the habitual roller's crawl (`driverProfile.rollingMPH = max(config.rollingMPH, 3 + 3 × aggression)`), so a rolled stop is plainly not a stop. The sandbox default is 0, which leaves the MIND-01 crawl of 3–6 mph.
- **Round start.** A round begins on a card over the map (round number, intersection, the watch list, "Click on any car that breaks the law") with a 3-2-1 countdown; traffic does not move and clicks are ignored until GO. Scripted stop and red runners prefer candidates that would pull out in front of a car with the right of way one to four seconds from the crossing (`cutsSomeoneOff`), and a barge is scripted only with a circulating conflict (`circleConflict`, 3.5 s). A car braking at the emergency limit leaves fading skid marks and flares its brake lights; on duty those cues and a pulsing "!" (1.6 s) appear only when the car that forced the stop is a registered rule breaker, so an ordinary emergency stop does not read as a violation. The map is drawn at `SCALE = 2.7 × ZOOM` (ZOOM 0.9, junction-geometry) and the in-view bounds `VIEW_X` / `VIEW_Y` follow it; a miss is called 12 ft or 0.8 s of travel before the edge.
- **Streaks (2026-11-01).** `cop.hot` counts good tickets in a row; a ticket is worth `(100 + 50 if quick) × min(hot, streakCap)` (`streakCap` 3, so ×1, ×2, then ×3). A wrong ticket (`strike`) or a miss sets `hot` to 0. `cop-ticket` events and the `busted` result carry `multiplier` and `hot`; the HUD shows a "×2 streak" chip beside the score, the ticket bubble and the log name the multiplier. The round bonus is unchanged.
- **Pulled over (2026-10-31).** A ticketed driver (`b.status === 'caught'`) finishes crossing; once it is `pullOverPastExitFeet` (6) past its exit and still in view, `copStep` brakes it (`car.pullingOver`, 20 ft/s² in `drive()`) and at a crawl calls `pullOver(state, car, excuse)` in collisions.mjs, which gives it a crash-shaped state (`car.crash.ticket`, no collision event, no crash counter) so the roadside planner parks it off the road like a crashed car, at `ticketPullOverMPH` (6) and for `ticketParkedSeconds` (30) before it drives off with its ticket. The excuse (`EXCUSES[kind]`, seeded by car id) becomes its thought once parked; `copStep` emits `pullover` and the log carries `cop-pullover`. When it parks, `policePose` (collisions.mjs) finds a spot for the police car a car length behind it along its heading, nudged away from the road until `offRoad` accepts the whole 16 × 6.5 ft body (`crash.copPose`; none found, no car drawn), so traffic never drives through the cruiser; the app draws it there (intersection.mjs `drawPoliceCars`, lights alternating 4 Hz, pulling up along the shoulder over 0.8 s after `parkedAt`), a speech bubble with the excuse for 5 s from a second after parking, and plays a siren blip at the stop. A driver already out of view, or wrecked, just keeps its ticket.
- **Daily shift (2026-10-31).** `daySeed(dayKey())` turns the UTC date into a run seed (FNV hash, 1–9999), so everyone gets the same traffic and scripted breakers; `cop.daily` holds the day, `?daily` opens on it, the panel button toggles it, the HUD and intro say Daily, and the shift posts to `traffic-sandbox:daily:<date>` (worker `boardSpec`, from 2026-10-31, never a future day) with "Daily shift · October 31 UTC" as the title. `copDaily` keeps the best for the day; `copBest` still counts every shift.
- **Interface.** The thought bubble, driver panel, pedals, Chaos Monkey, comparison and playback speed are hidden on duty; car numbers and speed labels stay available. Good tickets ring the car and whistle, wrong ones buzz, a held click shows "Watching car N…" (its verdict shows where the click landed if the car has gone), and a miss is called out loud: a red banner across the top of the map (its second line counts the run: "2 in a row", "three in a row"), a red bubble and a MISSED ring at the car, a falling two-note sound, and a line in the ticket log; a round that ends on a miss shows its card 2.6 s later so the call is seen. The HUD's MISSES LEFT counts down from 3 (green, orange at 2, red and pulsing at 1) and a ticket puts it back to 3. **Phones (≤ 740 px) on duty:** the map stage turns 90° (`fitMap`, `mapRotated`, `mapFrame`) so the longer street runs down the portrait screen and the whole world shows about half again as large, scaled to fit the width and the screen height; nothing in the engine changes (the view stays 172 × 104 ft). Pointer and bubble positions go through `stageFromScreen` / `screenFromStage`, the ticket bubble lives in the viewport (upright, unscaled), the MISSED label is drawn upright, and a coarse pointer gets a 60-stage-pixel tap radius (46 for a mouse). The HUD sticks to the top of the screen (`.mode-hub` is `display: contents` there) and carries the round's watch list as chips (`VIOLATIONS[kind].short`); the heading's eyebrow and subtitle, the mode switch, the scene toolbar, the lab card on the grass and the panel's title, copy and watch list are hidden; the ticket log stays below the map. Every round's watch list includes drivers who pull out or turn in front of someone. Speed labels are the player's choice, off by default: a speeder is obvious because it trails speed streaks (VIEW-01) and the HUD names the posted speed. Two pages, one app (2026-10-31): `/traffic-cop/` is the game and opens straight on duty (`<body data-page="cop">`, `COP_PAGE` in app.mjs; `enterCop(true)`, so the play counts at the first click), `/traffic-sandbox/` is the Traffic Playground, the lab, and opens straight into the intersection; neither has a start menu. Both are rendered from `traffic-sandbox/page.template.html` by `node tools/traffic-pages.mjs` (the Node suite checks they are current) and load the same `/traffic-sandbox/app.mjs` and stylesheet; `@cop` / `@sandbox` blocks carry each page's title, description, JSON-LD, prose and About copy. The Sandbox / Traffic Cop switch is two links; the old `?mode=cop` on the lab page redirects to the game page; a shared setup link opens the lab. On the game page Quit with nothing scored and "Back to the sandbox" go to the lab page, and "End shift" on a round card ends the shift properly (score card, board). The catalog entry keeps the id `traffic-sandbox` with `url: '/traffic-cop/'`, so the board (`PATH_ALIAS` maps `traffic-cop` to it in boards.js), the activity tracker, the profile row and GA events keep their history. The page wears the site's paper theme (the Dominoes chrome: logo, category link, Bricolage Grotesque titles); the map art is unchanged. Each finished round sends a `level_end` analytics event (round, passed, caught, quota, score) beside `level_start` and `game_over`. The shift-over card shows the score, each round's line, the feedback ask, the new-best moment (`traffic-cop-score`) and the public top ten (`LGBoards.mount`, board `traffic-sandbox`, named Traffic Cop, cap 1e6; boards.js also reads `copBest` from `trafficSandboxV1` so an older best is posted from the page's Top scores panel); the best shift persists in `copBest`. Below the lab, a visible "How to play Traffic Cop" section carries the rules, scoring and the sandbox description for readers and search engines; the page title and description lead with the game. `tools/traffic-cop-test.mjs` covers the ladder, classification, who had to yield, forced-brake breakers, held clicks, misses named in view, perfect and idle runs, strikes, repeats, late tickets and the sandbox staying script-free.

### SIGNAL-04 — Right on red

At timed and presence-controlled signals, a right-turning driver facing circular red must complete the one-second full stop at the line. It then checks its own sampled observations for occupied shared paths, priority arrivals (including protected left turns feeding its exit), and a blocked receiving lane. Arrival gaps use its individual `mainGap` and observation confidence. Stopped red-light queues are not treated as imminent priority arrivals; moving vehicles that cannot stop gently still count. Manual Hold keeps traffic stopped. Through and left movements retain their red-light restrictions.

Admissions include `rightOnRed`, stop duration, signal movement, driver personality, and perception evidence. Diagnostic stop checks apply, and a qualifying right-on-red admission is not labeled a signal violation. The car's thought identifies the full stop, yielding, or completed entry. Right-on-red and roundabout clearance changes advance the driving model and comparison keys to **junction-v14**. Historical v13 benchmark artifacts remain unchanged.

### VIEW-01 — Vehicle labels

Car numbers and live speed in MPH are independent saved display choices. When both are enabled, the ID and speed occupy separate lines in an upright label. Speed uses the current physical velocity, rounded to whole MPH, including zero when stopped. Labels do not change traffic, score, or the random sequence.

**Speed streaks.** A car more than 12% over the posted speed (the lower of the selected speed and 45 mph) trails three light streaks behind its rear bumper, longer and brighter up to 50% over. They read the car's real speed, so ordinary traffic (habits top out near 7% over) never shows them; a scripted speeder in Traffic Cop, a floored gas pedal or Chaos Monkey does. Crashed cars show none.

### MISTAKE-01 — Optional mistakes and observable signals

`mistakes` defaults to true to retain the existing attention/judgment behavior. False sets effective attention to 1, judgment to 0, rolling-stop habit to false, and deliberate late-red allowance to zero. It disables judgment scatter, decision wobble, distraction, forgotten signals, and wrong-lane assignment. Reaction/following personalities and physical collision/recovery remain active; manual Gas and Chaos remain explicit interventions. The UI restarts the same seeded traffic when the checkbox changes. Comparisons include the setting in their key; the five rated challenges fix it to true.

At creation, independently hashed driver/seed values select missed signals (2–14% of turning drivers, scaled by inattention) and wrong-lane turns (1–6.5% of eligible passenger-car turns, likewise scaled). These illustrative rates consume no arrival/turn randomness. A wrong-lane left starts in the through lane when a left pocket exists. A wrong-lane right starts in through when a separate right pocket exists; a shared through/right lane is legal and never treated as a mistake. Trucks and roundabouts are excluded from wrong-lane assignment.

Wrong-lane paths preserve the through-lane approach, curve continuously through the intersection, and join the ordinary receiving lane. They use the occupied through lane's signal, not a protected left arrow from a neighboring lane. Conflicts include same-approach vehicles because the wrong turn may cross their path. Public path candidates let observers infer a surprising turn from their own blinker memory, position, and heading, without reading the actual private route.

`vehicleCues` suppresses a forgotten indicator for both rendering and perception. Brake lights and crash hazards remain visible. Logs retain `mistakesEnabled`, `signalOmitted`, and `wrongLaneTurn`; admissions flag `missing-turn-signal` / `wrong-lane-turn` for review. These changes advance the model/comparison version to junction-v18. Tests cover continuous turns on every eligible approach, lane-correct signal behavior, missed-lamp observations, on/off traffic identity, and diagnostic evidence.

### AUDIO-01 — Read-only soundscape

`audio.mjs` reads positions, speed, acceleration, emergency-braking evidence, and collision counts. It never changes model state or its random streams. At most six nearby engine/tire voices play with spatial panning; horns/skids have wall-clock cooldowns and crash sounds play once for a new contact count. An original 112-BPM, 16-bar major-key instrumental score with lightly swung eighth notes, plucked melody/chords, bouncing bass, and bell accents uses a separate real-time scheduler, so faster simulation playback does not pitch-shift the music. Controls persist separately from traffic settings and scores. Music and effects are gated during pause, About/share/results, hidden tabs, and comparisons. Pending notes/effects are cleared on silence/reset. No external audio assets or network services are needed.
