# nigig-traffic remediation — implementation status (2026-09-26, round 3) Baseline: `EXECUTION_PLAN.md` (2026-09-12, audit commit `e899a27`). This file records what is DONE in-tree vs what still BLOCKS release. Nothing here supersedes ADR 001/002; assessment release stays blocked. ## Round 3 (2026-09-26) — TRAFFIC-P0-01 closed, first real build/test run - **Upstream fix landed** on the fork branch `andodeki/makepad@nigig-traffic-camera-boom-fix` (based on the previous pin `b580de1`, head of `nigig-makepad-test-android`; no history rewritten): - `1561856` — game/blocks controller, game/render scene and the gamemaker example pass the filmed body to the 5-arg `camera_boom_limit` (exactly `docs/UPSTREAM_FIX_camera_boom_limit.patch`, plus the third caller the patch missed in `examples/gamemaker`). - `a3d8f52` — once the boom error cleared, `makepad-game-render` exposed a second drift: `EmitterAnchor::EntityLocal` (added in game-sim) was not matched in `game/render/particles.rs` (E0004). Ported the already- shipped `libs/render` implementation (pose-aware `step`, `clear`); arcade caller now passes `(pos, yaw)`. - **Workspace re-pinned atomically**: every `andodeki/makepad` rev in `Cargo.toml` + `Cargo.lock` moved `b580de1` → `a3d8f521` (same tree plus the two fixes; no other dependency changed; `--locked` resolves). - **Crate-level compile break the broken pin had hidden**: `scenarios.rs` built a `Walk` literal missing 8 fields added upstream → now `..Walk::new(..)`. - **First executed test run ever** found 2 real failures in `rule_adversarial`: completion copy for roundabout (`exit`) and speed (`compliance`) tripped the banned-claim gate. Copy reworded ("route choice and give-way not assessed", "speed-limit behaviour not assessed"); gate NOT weakened. - **Clippy ratchet at zero**: 11 compiler warnings + 28 clippy findings in Traffic-owned code fixed (`Vec2` now implements `Add`/`Sub`, `matches!` transition table, enumerate loop, const assert, auto-fixes). Two intentional flat-scalar builders carry a justified `allow(clippy::too_many_arguments)`. ### Verification executed (round 3, Linux x86_64, rustc 1.97.1) | Command | Result | |---|---| | `cargo check --locked -p nigig-traffic --all-targets` | ✅ clean, 0 Traffic warnings | | `cargo test --locked -p nigig-traffic -- --test-threads=1` | ✅ **117 passed, 0 failed** (+4 GUI tests below, which need a display) | | — lib 84 · asset_validation 3 · coordinate_properties 4 · perf 1 · perf_release 1 · persistence_faults 7 · rule_adversarial 4 · runtime_ui 5 · scenario_replays 4 · ui_basic 4 | | | `xvfb-run -a cargo test --locked -p nigig-traffic --test ui` | ✅ **4/4 GUI tests pass**, 0 ignored | | `cargo clippy --locked -p nigig-traffic --all-targets` | ✅ exit 0, **0 Traffic-owned diagnostics** (CI ratchet baseline 0) | | `cargo fmt -p nigig-traffic -- --check` | ✅ | | `git diff --check` | ✅ | Note: plain `cargo clippy ... -- -D warnings` still fails on *other* workspace members (`matrix_client`, `nigig-core`, `nigig-uikit` warnings). That is why CI uses the Traffic-owned ratchet; those crates are out of Traffic scope. ## Done (in-tree, gated by CI) Round-2 closures (all partials advanced to their in-crate maximum): - TRAFFIC-01 (code-side maximum): `docs/UPSTREAM_FIX_camera_boom_limit.patch` carries the exact upstream fix, verified by reading both call sites: `Controller::tick` already has the filmed `subject: u64` in scope (its own docs: "`subject` is the entity being followed"), and `game/render/scene.rs` films `world.cam_third` exactly like the already-migrated non-game renderer. Application + re-pin + matrix steps are in the patch file. Push + re-pin need a human with fork access (see Blockers). - TRAFFIC-05 (wired): `TrafficWorld::evaluate` builds one immutable `Observation` on the course frame and the rule layer consumes only that (+ the live parking footprint passed alongside). No rule reads raw `GameWorld` state; observation/heading helpers are the single implementation shared by table and engine. - TRAFFIC-06 (stateful engine, practice scope): `RuleEngine` with ordered criteria, 30-tick speed grace / 30-tick calm re-arm, 60-tick parking dwell, edge-triggered lane/stop mistakes, immediate proximity terminals, reversal-required U-turns, and a bounded (4,096 + drops) evidence trace. Shares predicates + message constants with the one-tick table (which stays as the tested reference). 7 engine unit tests pin grace/dwell/edges/bounds/determinism. - TRAFFIC-07 (frozen): every Driving tick mixes gated input into a rolling FNV digest; terminal transitions freeze an immutable `AttemptResult` (ID + content version + digest) with a recompute-matches invariant, and HUD/score are set from the frozen value by construction. Batch-vs-incremental digest equivalence tested. - TRAFFIC-11 (in-crate maximum): attempts record into an in-memory session `LearnerProfile` (anonymous, compacted at 10k) on every terminal freeze; profile survives scenario switches, resets with the world, is never written to disk here, and settings copy stays session-only (honest). Filesystem persistence + advertised copy remain a later tranche with recovery tests. - TRAFFIC-13 (extended): hostile corpus battery in-tree (nesting, truncation, bracket-in-string, bad bounds, traversal ids, malformed UTF-8, empty objects, id-length edges, budget edges). `cargo fuzz` target + release SBOM remain scheduled work. Round-1 foundations (unchanged, see ADR-002 amendments): - TRAFFIC-00: `section_paywall`/`PaywallAction`/Upgrade dispatch deleted; Premium/FREE/locked sales copy gone; persistence claim replaced with session-only copy; every scenario labelled `Practice preview — finish marker only` (settings + scenarios page + drive HUD + lessons intro); unobservable-claim descriptions reworded with practice qualifiers; source-grep gate rejects fictional commerce/persistence copy. - TRAFFIC-01 (half): CI fails CLOSED on Cargo exit status (check gate + clippy gate both fail on dependency errors; stderr scanned for `^error`). Third-party actions pinned to immutable SHAs. Allowed-source + license-presence supply-chain gates added. **Upstream pin repair still open** (see Blockers). - TRAFFIC-02: `object_type` recursion fixed + delegation test; `Scenario` validation (IDs, finite coords, positive max_points, course length, speed limits) + `validate_catalog` + malformed-fixture tests; content version 0 (`LegacyUnvalidated`), jurisdiction `unreviewed-general`, FNV seeds, capability lists; persistence binds ID+version, never index. - TRAFFIC-03: typed `AuthoredPoint2`/`CoursePoint`/`Heading`/`CourseFrame` with inverse conversions; lane rule uses course-lateral only; `course_lateral`/`course_along` are the only legal rule inputs; idle false-drift deleted and replaced with a zero-drift regression over all lane scenarios + round-trip/heading property tests. - TRAFFIC-04: `Phase::{Intro,Driving,Paused,Complete,Failed}` with `drive_input_allowed`/`is_terminal` + `allowed_transition` table; Intro ticks without simulating; terminal/paused freeze; no-scenario stays Intro; input cleared on focus loss/switch/restart/terminal; determinism + transition-table + pause + head-start + stability tests. - TRAFFIC-05: immutable per-tick `Observation` builder (course pose, relative heading, speed, lane offset, finish, hazard proximity, tick); oriented-rect containment + documented proximity-only clearance; proximity is NEVER labelled contact. - TRAFFIC-06 (practice scope): U-turn uses course-relative reversal (world-x bug fixed + invariance test); proximity wording fixed; completion strings say PRACTICE and never confirm unobserved behaviour (adversarial suite pins banned strings); finish-only shortcuts complete as labelled practice. - TRAFFIC-07: `ScorePolicy` (validated max, −5/mistake, floor 0, 70% pass) + immutable `AttemptResult` + replay digest; `max_points` comes from the catalog (test iterates all 47); Score↔ScorePolicy agreement tested; HUD shows `score/max` from the scenario. - TRAFFIC-08 (stage 1): 47-row `CURRICULUM_MATRIX.md` with real-vs- reserved observations, replay mapping, and `unreviewed` reviewer column; hazard actors added for child/branch/rain; sign/stop-line/ indicator/handbrake/kerb claims removed or practice-qualified. **Qualified expert sign-off still open.** - TRAFFIC-09: `InputRouter` (keyboard/touch/controller → clamped `DriveIntent`, brake-beats-throttle, touch brake never reverses); touch halves (left steer / right pedals) + finger cancellation in the view; P pause; equivalent-input cross-source tests. - TRAFFIC-10: 5-tick catch-up cap with drop counter; continuous frames only in Intro/Driving; Paused/terminal schedule nothing; accumulator reset on focus return; render-mode changes stay presentational; `MAX_TICKS_PER_FRAME` + touch-curve unit tests. - TRAFFIC-11: versioned atomic `LearnerProfile`/`AttemptRecord` (temp+flush/sync+rename+verify), Absent/Corrupt-preserved/Future-read- only/IoError states, 8 MiB + 10k compaction, traversal/oversize/ malformed rejection, round-trip WITH attempts, fault-injection suite. Round 2 wires session recording into the world (see above); filesystem persistence + advertised copy remain a later tranche. - TRAFFIC-12: `AssetManifest` (ID validation, license/hash/bounds, traversal rejection); no-op seam + `CARGO_MANIFEST_DIR` probe deleted; explicit primitive fallback with stable log marker; catalog declares no hard visual dependencies (test-pinned). - TRAFFIC-13: `THREAT_MODEL.md` (offline scope, supply chain, parser bounds, privacy, unsafe scope, residual risks); `SBOM.md` release template; CI source/license gates. - TRAFFIC-14: GUI harness documented with owner/expiry ignores (release stays blocked); `runtime_ui` headless production-path journeys; `ui_basic` labelled smoke and extended to all 47 scenarios; replay/adversarial/property suites named per the plan matrix. - TRAFFIC-15: `perf_release` active-workload evidence (reported, never asserted) + structural budget asserts; `PERF_BASELINE.md` corrected (median-of-idle-means, diagnostic-only labelling) + release-evidence template with §6 budgets. ## Blockers (release stays RED) Only human/environment gates remain. No further in-crate work is identified for practice-preview scope: 1. ~~TRAFFIC-P0-01~~ — **CLOSED in round 3** (see above). Remaining follow-up: open a PR from `nigig-traffic-camera-boom-fix` into the fork's main line so the pin is not a side branch forever. 2. ~~TRAFFIC-14 harness~~ — **CLOSED in round 3**. Root cause of "exit 101 before AppStarted" was the broken pin; the remaining startup SIGSEGV was simply running without a display. Under `xvfb-run -a` all 4 `tests/ui.rs` GUI tests pass, ignores removed, CI step added (with `xvfb`, `libgl1-mesa-dri`). The HUD test was rewritten: HUD strings are raw `draw_text` (not widgets) so text selectors cannot see them; it now asserts the 3D view survives Intro→Driving and captures a screenshot (manually verified: HUD shows `Speed: 0 km/h Score: 0/100`). Follow-up UX note: on a 430 px phone width the HUD header/help lines are clipped at the right edge — wrap or shorten them. Mobile on-device touch journey still needs a real/emulated device. 3. TRAFFIC-08 sign-off — no qualified instructor/law review; content v0. Assessment release additionally needs per-variant rule supersession with reviewed copy, which is real curriculum work, not code volume. 4. TRAFFIC-15 device lab — no release/GPU/latency/soak numbers yet; run `perf_release` + stats on declared hardware after blocker 1. ## Verification actually executed (2026-09-25) Full-workspace `cargo check/test` is impossible until blocker 1 clears (dependency crates do not compile). Verified instead: - `cargo fmt -p nigig-traffic -- --check`: CLEAN (also parses every edited/added file — syntax valid). - `git diff --check`: clean. CI grep gates reproduced locally: no `unwrap/expect` in `src/` (fixed 5 violations incl. a latent `PartialEq` gap below), no `unsafe`, no TODO, no fictional commerce/persistence copy, YAML workflow parses (ruby). - Standalone harnesses in `/tmp` (`traffic-pure{,2,3}`) compiling the BYTE-IDENTICAL real files for every std-only module: **49 tests executed, all pass** — pure: geom 3, input 4, observation 3, assets 3, persistence 5 (incl. attempts round-trip + hostile battery); pure2: scoring 5 (incl. incremental-digest equivalence); pure3: geom 3, observation 3, objective 20 (13 table + 7 engine: course-relative U-turn invariance, grace/dwell/edge/bound/determinism pins). The harness caught and fixed one latent bug the broken pin had been hiding: `LoadOutcome: PartialEq` could not compile because `LearnerProfile` lacked `PartialEq`. - Description gate re-checked with an independent script: 20 `description:` fields, 0 unobservable-claim violations; 2 titles softened (`(Handbrake)` dropped, three-point renamed). - NOT executed (blocked): `world.rs`/`scenario.rs`/`road.rs`/ `game_view.rs`/page unit tests, all 8 integration suites, clippy. They were reviewed field-by-field against public APIs instead (see per-item notes in the session); run the §9 matrix on a clean coherent checkout before any release claim. Practice-preview release may proceed only after (1) lands and the full §9 matrix is green on a clean checkout; assessment release additionally needs (2)–(4) plus per-variant rule supersession with reviewed copy.