id: 483eaebb6f7745e997075b942363c050
parent_id: 5d8afd31e7454b43865c8d154ed0af8a
item_type: 1
item_id: 6c0dcb2a567348fd9796f50c790082e4
item_updated_time: 1780472119461
title_diff: "[]"
body_diff: "[{\"diffs\":[[0,\"e — \"],[-1,\"Independent Machine Deployment\\\n\\\nPlugins run on the game machines; the Rust receiver runs on a separate analysis machine. DR2 and Project CARS have native UDP output — no code needed on those machines.\\\n\\\n```\\\nGame Machine (AC / Proton)                Analysis Machine\\\n\"],[1,\"Dual-Source Telemetry\\\n\\\n### Two Independent Data Sources for AC\\\n\\\nAC provides telemetry through two independent channels that complement each other:\\\n\\\n```\\\n┌─────────────────────────────────────────────────────────────┐\\\n│                    GAME MACHINE (AC / Proton)                │\\\n│                                                             │\\\n│  \"],[0,\"┌───\"]],\"start1\":3603,\"start2\":3603,\"length1\":273,\"length2\":356},{\"diffs\":[[0,\"─┐  \"],[-1,\"            ┌────────────\"],[1,\"┌\"],[0,\"────\"]],\"start1\":3981,\"start2\":3981,\"length1\":33,\"length2\":9},{\"diffs\":[[0,\"───────┐\"],[1,\" │\"],[0,\"\\\n│ \"],[1,\" │ AC Built-in Engine        │  │\"],[0,\" ac-tele\"]],\"start1\":4004,\"start2\":4004,\"length1\":19,\"length2\":54},{\"diffs\":[[0,\"    \"],[1,\" \"],[0,\"│ \"],[-1,\"             │  rusty-telemetry                    │\\\n│  (Lua plugin inside AC)  │──UDP :5005──→│                               \"],[1,\"│\\\n│  │ (always runs, no plugin)  │  │ (CSP Lua app)            │ │\\\n│  │                           │  │                           │ │\\\n│  │ Reads: shared memory\"],[0,\"      │\"],[-1,\"\\\n│ \"],[1,\"  │\"],[0,\" Reads\"],[-1,\" ac.* API, sends   │   JSON       │  ┌\"],[1,\": CSP Lua API        │ │\\\n│  │ Format: binary (undoc.)   │  │ Format: JSON (defined)    │ │\\\n│  │ Rate: ~20Hz fixed         │  │ Rate: configurable 60Hz   │ │\\\n│  │ Target: 127.0.0.1:9996    │  │ Target: configurable IP   │\\\n│  └──────────┬────\"],[0,\"────\"]],\"start1\":4071,\"start2\":4071,\"length1\":191,\"length2\":425},{\"diffs\":[[0,\"┬───────────────\"],[-1,\"┐  ┌\"],[1,\"┘  └\"],[0,\"────────────────\"]],\"start1\":4487,\"start2\":4487,\"length1\":36,\"length2\":36},{\"diffs\":[[0,\"────\"],[1,\"┬─\"],[0,\"──────\"],[-1,\"┐  │\\\n│  JSON over network       │\"],[1,\"─────────┘ │\\\n│             │ UDP :9996\"],[0,\"    \"]],\"start1\":4513,\"start2\":4513,\"length1\":47,\"length2\":54},{\"diffs\":[[0,\"    \"],[-1,\"│\"],[0,\"  \"],[-1,\"│ Receiver  │→ │ Parser-AC \"],[1,\"    │ UDP :5005\"],[0,\"     \"],[-1,\"│\"],[0,\"  \"],[1,\" \"],[0,\"│\\\n└─\"]],\"start1\":4573,\"start2\":4573,\"length1\":46,\"length2\":33},{\"diffs\":[[0,\"│\\\n└─────────────\"],[1,\"┼─────────\"],[0,\"─────────────┘  \"]],\"start1\":4602,\"start2\":4602,\"length1\":32,\"length2\":42},{\"diffs\":[[0,\"────────────────\"],[-1,\"┘\"],[1,\"────────┼─────────────────┘\\\n\"],[0,\"              │ \"]],\"start1\":4625,\"start2\":4625,\"length1\":33,\"length2\":60},{\"diffs\":[[0,\" │  \"],[-1,\"│ (multi-\"],[1,\"         \"],[0,\"   \"],[-1,\"│\"],[0,\"  \"],[-1,\"│ (JSON → model) │\"],[1,\"            \"],[0,\"  │\\\n\"]],\"start1\":4682,\"start2\":4682,\"length1\":41,\"length2\":34},{\"diffs\":[[0,\"            \"],[-1,\"        \"],[1,\"▼\"],[0,\"            \"]],\"start1\":4718,\"start2\":4718,\"length1\":32,\"length2\":25},{\"diffs\":[[0,\"    \"],[-1,\"│\"],[1,\"   \"],[0,\"  \"],[-1,\"│\"],[0,\"  \"],[-1,\"port UDP)│  ├\"],[1,\"   ▼\\\n┌──────\"],[0,\"────\"]],\"start1\":4747,\"start2\":4747,\"length1\":27,\"length2\":27},{\"diffs\":[[0,\"────\"],[-1,\"┤  │\\\nGame Machine (DR2 / native)               │  │           │→ │ Parser-DR2     │  │\\\n┌──────────────────────────┐\"],[1,\"───────────────────────────────────────┐\\\n│                    ANALYSIS MACHINE                          │\\\n│                    rusty-telemetry                           │\\\n│\"],[0,\"    \"]],\"start1\":4782,\"start2\":4782,\"length1\":123,\"length2\":180},{\"diffs\":[[0,\"\\\n│              \"],[-1,\"│  │\"],[0,\"           │  │ \"]],\"start1\":4956,\"start2\":4956,\"length1\":36,\"length2\":32},{\"diffs\":[[0,\"    \"],[-1,\"│\"],[0,\"  \"],[-1,\"│ (binary→model) │  │\\\n│  Dirt Rally 2.0          │──UDP :5015──→│\"],[1,\"                     \"],[0,\"  \"],[-1,\"│\"],[0,\"    \"]],\"start1\":4979,\"start2\":4979,\"length1\":79,\"length2\":33},{\"diffs\":[[0,\"       │\"],[-1,\"  ├\"],[1,\"\\\n│  ┌─────────────────────────────────────\"],[0,\"────────\"]],\"start1\":5012,\"start2\":5012,\"length1\":19,\"length2\":58},{\"diffs\":[[0,\"────\"],[-1,\"┤\"],[1,\"┐ \"],[0,\"  │\\\n│  \"],[-1,\"(native UDP output,     │  Binary\"],[1,\"│              Multi-Port UDP Receiver                 │   │\\\n│  │  :9996 ──→ parser-ksudp ──┐         \"],[0,\"     \"],[-1,\" │  │\"],[0,\"    \"]],\"start1\":5074,\"start2\":5074,\"length1\":59,\"length2\":124},{\"diffs\":[[0,\"   │\"],[-1,\"→ │ Parser-PCARS  \"],[1,\"   │\\\n│  │  :5005 ──→ parser-ac    ──┼──→ Core Telemetry Model\"],[0,\" │  \"],[1,\" \"],[0,\"│\\\n│  \"],[-1,\" no plugin needed)      │              │  │           │  │ (binary→model)\"],[1,\"│  :5015 ──→ parser-dr2   ──┤                         │   │\\\n│  │  :5006 ──→ parser-pcars ──┘                        \"],[0,\" │\"],[1,\" \"],[0,\"  │\\\n\"],[-1,\"└\"],[1,\"│  └───────────────────\"],[0,\"────\"]],\"start1\":5202,\"start2\":5202,\"length1\":115,\"length2\":225},{\"diffs\":[[0,\"────────\"],[-1,\"┘              │  └\"],[1,\"────────┘   │\\\n│                                                             │\\\n│  ┌──────────────┐  ┌\"],[0,\"────────\"]],\"start1\":5441,\"start2\":5441,\"length1\":35,\"length2\":116},{\"diffs\":[[0,\"────\"],[-1,\"┘  └\"],[1,\"┐  ┌\"],[0,\"───────\"],[-1,\"┬\"],[0,\"────────\"],[-1,\"┘ \"],[1,\"───────┐\"],[0,\" │\\\n\"],[1,\"│\"],[0,\"  \"],[-1,\"     \"],[1,\"│ Plausibility │  │ Analyzer\"],[0,\"  \"],[1,\"│\"],[0,\"  \"],[-1,\"  \"],[1,\"│ AI Coach\"],[0,\"    \"]],\"start1\":5556,\"start2\":5556,\"length1\":46,\"length2\":84},{\"diffs\":[[0,\"        \"],[-1,\"    \"],[1,\"│ │\\\n│  │ Checker\"],[0,\"      \"],[1,\"│\"],[0,\"  \"],[-1,\" \"],[1,\"│ Engine\"],[0,\"   \"],[1,\" \"],[0,\"│  \"],[-1,\" \"],[1,\"│ (GLM-5.1)\"],[0,\"        \"]],\"start1\":5641,\"start2\":5641,\"length1\":36,\"length2\":67},{\"diffs\":[[0,\"    \"],[-1,\"          │           │\\\nGame Machine (PCARS / native)\"],[1,\"│ │\\\n│  │ (cross-ref)  │  │           │  │          \"],[0,\"    \"]],\"start1\":5708,\"start2\":5708,\"length1\":61,\"length2\":59},{\"diffs\":[[0,\"                \"],[1,\"│\"],[0,\" │\"],[1,\"\\\n│\"],[0,\"  \"],[-1,\"┌\"],[1,\"└\"],[0,\"──────────────┐ \"]],\"start1\":5759,\"start2\":5759,\"length1\":37,\"length2\":40},{\"diffs\":[[0,\"────\"],[-1,\"┐  ┌\"],[1,\"┘  └\"],[0,\"───\"],[-1,\"▼\"],[0,\"────────\"],[-1,\"┐\"],[1,\"┘\"],[0,\"  \"],[1,\"└──────────────────────┘\"],[0,\" │\\\n\"],[-1,\"┌\"],[1,\"└\"],[0,\"────\"]],\"start1\":5793,\"start2\":5793,\"length1\":31,\"length2\":54},{\"diffs\":[[0,\"────\"],[-1,\"┐              │  │ Storage      │  │ Analyzer   │   │\\\n│  Project CARS 1 & 2      │──UDP :5006──→│  │ (SQLite)     │←─│ Engine     │   │\\\n│  (native UDP output,     │  Binary      │  └──────────────┘  └────────────┘   │\\\n│   no plugin needed)      │              │  ┌──────────────┐  ┌────────────┐   │\\\n│  Config: UDP Protocol    │              │  │ REST + WS    │  │ Web        │   │\\\n│  Version = \\\"PCARS 1\\\"     │              │  │ API (Axum)   │→ │ Dashboard  │   │\\\n└──────────────────────────┘              │  └──────────────┘  └────────────┘   │\\\n                                          │  ┌──────────────┐                   │\\\n                                          │  │ AI Coach     │                   │\\\n                                          │  │ (GLM-5.1)    │                   │\\\n                                          │  └──────────────┘                   │\\\n                                          └─────────────────────────────────────┘\\\n```\"],[1,\"───────────────────────────────────┘\\\n```\\\n\\\n### Why Dual-Source?\\\n\\\n| Aspect | Port 9996 (built-in) | Port 5005 (our plugin) |\\\n|---|---|---|\\\n| **Availability** | Always runs, no install needed | Requires CSP + Lua app enabled |\\\n| **Reliability** | Hardcoded in AC binary, never crashes | Lua script can crash (like current bug) |\\\n| **Format** | Binary, community-reverse-engineered | JSON, we define the schema |\\\n| **Config** | Hardcoded `127.0.0.1:9996` | Configurable host/port |\\\n| **Rate** | ~20 Hz fixed | Configurable (60 Hz default) |\\\n| **Data depth** | Basic telemetry (speed, inputs, G, position) | Extended (tyre slip, temps, pressures, damage) |\\\n| **Cross-machine** | ❌ Localhost only | ✅ Any LAN IP |\\\n| **Coaching tier** | Basic + intermediate | Advanced (tyre management, traction analysis) |\\\n\\\n### Coaching Capability by Data Source\\\n\\\n| Coaching Use Case | Built-in (9996) | Plugin (5005) | Combined |\\\n|---|---|---|---|\\\n| **Lap time analysis** | ✅ Sufficient | ✅ Sufficient | ✅ Cross-validate lap times |\\\n| **Braking point coaching** | ✅ Sufficient | ✅ Sufficient | ✅ Brake onset plausibility |\\\n| **Racing line analysis** | ⚠️ Adequate (has world pos + G) | ⚠️ Adequate (has G, no world pos yet) | ✅ World pos from 9996 + tyre slip from 5005 |\\\n| **Throttle application** | ⚠️ Adequate (wheel speed proxy) | ✅ Sufficient (wheel_slip) | ✅ Wheel speed vs wheel slip cross-check |\\\n| **Corner entry/exit** | ⚠️ Adequate (steering vs G, ~70% confidence) | ✅ Sufficient (wheel_slip, ~95% confidence) | ✅ G-force method validated by slip data |\\\n| **Tyre management** | ❌ No tyre data | ✅ Full tyre temps/pressures | ✅ Only from plugin |\\\n| **Damage monitoring** | ❌ No damage data | ✅ 5-zone damage | ✅ Only from plugin |\\\n\\\n### Data Plausibility Checking\\\n\\\nCross-referencing the two sources enables automatic data quality validation:\\\n\\\n| Check | Method | Detects |\\\n|---|---|---|\\\n| **Speed comparison** | `abs(speed_9996 - speed_5005) < threshold` | Plugin reading error, stale data |\\\n| **Input correlation** | `throttle_9996 ≈ throttle_5005` | Plugin desync |\\\n| **Lap time match** | `lap_time_9996 == lap_time_5005` | Timing drift between sources |\\\n| **G-force consistency** | `G_LAT_9996 ≈ g_forces.y_5005` | Coordinate system mismatch |\\\n| **Suspension correlation** | `susp_9996[i] ≈ suspension_travel_5005[i]` | Wheel index mapping error |\\\n| **Packet rate monitoring** | Track pkts/sec on each port | Plugin crash detection (9996 keeps flowing) |\\\n\\\nIf the plugin crashes (like the current LuaSocket bug), the system automatically falls back to port 9996 data and flags the plugin as offline — basic coaching continues uninterrupted.\\\n\\\n### AC Built-in UDP Protocol (Port 9996) — Reference\\\n\\\n**Status**: Binary protocol reverse-engineered by community. Not documented in the AC installation. The packet structure mirrors AC's shared memory layout (used by CrewChief, SIM Dashboard, SIM Hub).\\\n\\\n**Confirmed fields** (from AC log telemetry channels + community documentation):\\\n\\\n| Field | Type | Rate | Notes |\\\n|---|---|---|---|\\\n| packetId | int32 | — | Sequential packet counter |\\\n| speedKmh | float | ~20 Hz | Vehicle speed |\\\n| rpm | int32 | ~20 Hz | Engine RPM |\\\n| gear | int32 | ~20 Hz | Current gear (-1=N, 0=R, 1-6=forward) |\\\n| gas | float | ~20 Hz | Throttle 0–1 |\\\n| brake | float | ~20 Hz | Brake 0–1 |\\\n| clutch | float | ~20 Hz | Clutch 0–1 |\\\n| steerAngle | float | ~20 Hz | Steering angle (radians) |\\\n| G_LAT | float | ~20 Hz | Lateral G-force |\\\n| G_LON | float | ~20 Hz | Longitudinal G-force |\\\n| splinePosition | float | ~20 Hz | Normalized track position 0–1 |\\\n| worldPosition | vec3 | ~20 Hz | World coordinates (x, y, z) |\\\n| suspensionTravel[4] | float×4 | **200 Hz** | Per-wheel suspension travel |\\\n| wheelSpeed[4] | float×4 | ~20 Hz | Per-wheel speed |\\\n| lapTime | float | ~20 Hz | Current lap time (ms) |\\\n| lapCount | int | ~20 Hz | Current lap number |\\\n| fuel | float | ~20 Hz | Fuel level 0–1 |\\\n| bestLap | float | — | Best lap time (ms) |\\\n| lastLap | float | — | Last completed lap time (ms) |\\\n\\\n**Limitations for coaching:**\\\n- No wheel slip / tyre slip data → understeer/oversteer detection is inferential only (~70% confidence)\\\n- No vertical G-force → bump/aero analysis not possible\\\n- No tyre temperatures → tyre management coaching impossible\\\n- No tyre pressures → tyre strategy coaching impossible\\\n- No damage data → damage-aware strategy not possible\\\n- Hardcoded to localhost:9996 → requires game machine to relay for cross-machine use\\\n\\\n### Server Plugin Protocol (Port 11000) — Reference\\\n\\\n**Fully documented** in `sdk/dev/acRemoteServerUDP_Example/`. This is a different protocol for server-side plugins (multiplayer monitoring, race control). Provides:\\\n- `ACSP_CAR_UPDATE` — position, velocity, gear, RPM, spline for ALL cars\\\n- `ACSP_LAP_COMPLETED` — lap times with cuts, grip level\\\n- `ACSP_CLIENT_EVENT` — collisions (car-car, car-environment) with impact speed\\\n- `ACSP_SESSION_INFO` — session type, track, weather\\\n\\\nOnly available when running an AC server instance.\"],[0,\"\\\n\\\n--\"]],\"start1\":5865,\"start2\":5865,\"length1\":968,\"length2\":4992},{\"diffs\":[[0,\"a`\\\n\\\n\"],[-1,\"**The app deployment has uncommitted local modifications**: The `manifest.ini` and `main.lua` were manually edited in the apps/lua copy to add `[WINDOW_...]`, `[CORE]`, `LAZY = FULL`, and `script.windowMain`. These changes were never committed to git.\\\n\\\n\"],[0,\"####\"]],\"start1\":12242,\"start2\":12242,\"length1\":261,\"length2\":8},{\"diffs\":[[0,\"\\\n###\"],[-1,\"# Implementation details:\\\n- Built from scratch (not forked from ac-tracer — simpler approach for Phase 1)\\\n- Purpose-built `string.format`-based JSON serializer — zero table allocation, zero double traversal\\\n- Pre-allocated buffer tables at module scope, updated in-place each frame (zero GC pressure at 60Hz)\\\n- Named `do_send()` function avoids per-frame closure allocation\\\n- Uses `os.time()` for Unix epoch timestamps\\\n- Lap count from `car.lapCount` / `car.lapIndex` (not `session.laps`)\\\n- Suspension travel fallbacks: `car.wheels[].suspensionTravel` → `car.suspensionTravel` → `car.wheelSuspensionTravel`\\\n- Graceful fallback when `ac` global is absent (testing outside AC)\\\n- Configurable target host/port via `config.lua`\\\n\\\n**Files:**\\\n```\\\nac-telemetry-plugin/\\\n├── manifest.ini          (CSP app descriptor)\\\n├── icon.png              (app bar icon)\\\n├── config.lua            (UDP target: 127.0.0.1:5005, 60Hz interval)\\\n├── main.lua              (Entry point — wires script.update + windowMain)\\\n├── src/\\\n│   ├── json.lua          (Purpose-built telemetry serializer)\\\n│   └── telemetry.lua     (CSP API reader + UDP sender)\\\n└── .gitignore\\\n```\\\n\\\n### Repo 2: `rusty-telemetry` (Main Rust Backend)\\\n\\\n| Field | Value |\\\n|---|---|\\\n| **Language** | Rust |\\\n| **IDE** | RustRover |\\\n| **Local path** | `~/RustroverProjects/rusty-telemetry` |\\\n| **Remote** | `ssh://jan@192.168.1.2/home/jan/Development/Repositories/rusty-telemetry.git` |\\\n| **Runs on** | Analysis machine (independent from game machines) |\\\n| **Stack** | Tokio (async), Serde (JSON), Chrono |\\\n| **Status** | **Phase 1.1 complete** (simple listener) — commit `30543fd` |\\\n\\\n**Implementation details (Phase 1 — simple listener):**\\\n- Single-crate structure for now (workspace split deferred to Phase 1.2)\\\n- Async UDP listener on configurable bind address (`RUSTY_TELEMETRY_BIND` env var, default `127.0.0.1:5005`)\\\n- `#[serde(deny_unknown_fields)]` on both `TelemetryFrame` and `GForces` — schema drift causes immediate parse errors\\\n- Locked stdout writes (single lock per frame, amortized syscalls)\\\n- `ReceiveStats` tracker: packet count, pkt/s rate, KB/s data rate\\\n\\\n**Files (Phase 1):**\\\n```\\\nrusty-telemetry/\\\n├── Cargo.toml            (tokio, serde, serde_json, chrono)\\\n├── src/\\\n│   ├── main.rs           (UDP listener + banner + output loop)\\\n│   ├── telemetry.rs      (TelemetryFrame + GForces structs with Display)\\\n│   └── stats.rs          (ReceiveStats tracker)\\\n└── .gitignore\\\n```\"],[1,\" Repo 2: `rusty-telemetry` (Main Rust Backend)\\\n\\\n| Field | Value |\\\n|---|---|\\\n| **Language** | Rust |\\\n| **IDE** | RustRover |\\\n| **Local path** | `~/RustroverProjects/rusty-telemetry` |\\\n| **Remote** | `ssh://jan@192.168.1.2/home/jan/Development/Repositories/rusty-telemetry.git` |\\\n| **Runs on** | Analysis machine (independent from game machines) |\\\n| **Stack** | Tokio (async), Serde (JSON), Chrono |\\\n| **Status** | **Phase 1.1 complete** (simple listener) — commit `30543fd` |\"],[0,\"\\\n\\\n##\"]],\"start1\":12816,\"start2\":12816,\"length1\":2437,\"length2\":482},{\"diffs\":[[0,\"elemetry model)\\\n\"],[1,\"│   ├── parser-ksudp/             (AC binary KSUDP → generic telemetry model)  ← NEW\\\n\"],[0,\"│   ├── parser-d\"]],\"start1\":13609,\"start2\":13609,\"length1\":32,\"length2\":117},{\"diffs\":[[0,\"elemetry model)\\\n\"],[1,\"│   ├── plausibility/             (cross-source data validation)                ← NEW\\\n\"],[0,\"│   ├── storage/\"]],\"start1\":13855,\"start2\":13855,\"length1\":32,\"length2\":118},{\"diffs\":[[0,\"ng\\\n\\\n\"],[-1,\"A third repo would only be needed if a game requires a **relay process on the game machine** (e.g., ACC shared-memory-to-UDP bridge on Windows). That would be `rusty-acc-relay`, but YAGNI until ACC is actually added.\\\n\\\n\"],[0,\"---\\\n\"]],\"start1\":15060,\"start2\":15060,\"length1\":226,\"length2\":8},{\"diffs\":[[0,\"s 1–6)\\\n\\\n\"],[1,\"- **1.0 KSUDP Parser (port 9996)** — **NEW** — Can start immediately, no plugin dependency\\\n  - [ ] Implement `parser-ksudp` crate with binary parser for AC's built-in UDP\\\n  - [ ] Map to generic telemetry model (speed, inputs, G-forces, position, lap data)\\\n  - [ ] Listen on port 9996 alongside port 5005\\\n  - [ ] Enables basic coaching (lap times, braking points, racing line) from day one\\\n  - [ ] Works even if Lua plugin is broken — always-available fallback\\\n\\\n\"],[0,\"- **1.1 \"]],\"start1\":16103,\"start2\":16103,\"length1\":16,\"length2\":477},{\"diffs\":[[0,\"3900d2a`\"],[-1,\", pushed 2026-06-01\"],[0,\")\\\n  - [x\"]],\"start1\":16642,\"start2\":16642,\"length1\":35,\"length2\":16},{\"diffs\":[[0,\"] **\"],[-1,\"VERIFY: Check CSP log after fix** — confirm no more Lua errors, UDP packets flow\"],[1,\"ADD: world position + spline position + fuel** to telemetry output (available from CSP API)\"],[0,\"\\\n\\\n- \"]],\"start1\":17551,\"start2\":17551,\"length1\":88,\"length2\":99},{\"diffs\":[[0,\"3fd`\"],[-1,\", pushed 2026-06-01\"],[0,\")\\\n  \"]],\"start1\":17722,\"start2\":17722,\"length1\":27,\"length2\":8},{\"diffs\":[[0,\"rser-ac`\"],[1,\", `parser-ksudp`\"],[0,\" crates\\\n\"]],\"start1\":18073,\"start2\":18073,\"length1\":16,\"length2\":32},{\"diffs\":[[0,\"er (\"],[-1,\"sepa\"],[1,\"ports 9996, 5005, 5015, 5006)\\\n  - [ ] `plausibility` c\"],[0,\"rate \"],[-1,\"ports for DR2 and PCARS)\"],[1,\"for cross-source data validation\"],[0,\"\\\n  -\"]],\"start1\":18134,\"start2\":18134,\"length1\":41,\"length2\":99},{\"diffs\":[[0,\"ture\"],[-1,\" (Protocol Version \\\"Project Cars 1\\\")\"],[0,\"\\\n  -\"]],\"start1\":18631,\"start2\":18631,\"length1\":44,\"length2\":8},{\"diffs\":[[0,\"odel\"],[-1,\"\\\n  - [ ] Verify PCARS 1 and PCARS 2 packet compatibility (shared protocol base)\"],[0,\"\\\n\\\n##\"]],\"start1\":18670,\"start2\":18670,\"length1\":87,\"length2\":8},{\"diffs\":[[0,\"os, \"],[-1,\"derivatives (jerk, slip angle)\"],[1,\"curvature (G_LAT / v²), derivatives\\\n- **Plausibility engine**: cross-reference port 9996 vs port 5005 data\"],[0,\"\\\n- D\"]],\"start1\":18764,\"start2\":18764,\"length1\":38,\"length2\":114},{\"diffs\":[[0,\"ing \"],[-1,\"and sector analysis\\\n- Export: CSV, MoTeC compatible format\"],[1,\"from world position data\\\n- Reference lap system using spline position\"],[0,\"\\\n\\\n##\"]],\"start1\":19000,\"start2\":19000,\"length1\":66,\"length2\":77},{\"diffs\":[[0,\"user)\\\n- \"],[1,\"**\"],[0,\"Track ma\"]],\"start1\":19212,\"start2\":19212,\"length1\":16,\"length2\":18},{\"diffs\":[[0,\"ith \"],[-1,\"position trail\"],[1,\"racing line** (world position from port 9996)\"],[0,\"\\\n- T\"]],\"start1\":19233,\"start2\":19233,\"length1\":22,\"length2\":53},{\"diffs\":[[0,\" 19–24)\\\n\"],[1,\"- **Basic coaching** (from port 9996 data): braking points, lap consistency, racing line\\\n- **Advanced coaching** (from port 5005 data): traction management, tyre strategy\\\n\"],[0,\"- Featur\"]],\"start1\":19430,\"start2\":19430,\"length1\":16,\"length2\":187},{\"diffs\":[[0,\"hing\"],[-1,\"\\\n- Coaching tips displayed in web interface\\\n- Reference lap upload & correlation\"],[0,\"\\\n\\\n--\"]],\"start1\":19770,\"start2\":19770,\"length1\":88,\"length2\":8},{\"diffs\":[[0,\" |\\\n\\\n\"],[-1,\"### Kilo Code Prompt Patterns\\\n- **Architect Mode**: Design schemas, data flow, Protobuf definitions\\\n- **Code Mode**: Generate boilerplate (UDP listener, Lua plugin structure, parsers)\\\n- **Debug Mode**: Analyze errors from Rust compiler or Lua runtime\\\n\\\n\"],[0,\"---\\\n\"]],\"start1\":20203,\"start2\":20203,\"length1\":260,\"length2\":8},{\"diffs\":[[0,\"ntation\\\n\"],[1,\"- **AC Server Plugin Protocol** — fully documented in `sdk/dev/acRemoteServerUDP_Example/`\\\n- **AC Shared Memory reverse-engineering** — `raphael-rial/ACSharedMemory` and `compujuckel/AssettoCorsaSharedMemory` on GitHub (port 9996 binary structure)\\\n\"],[0,\"- `acevo\"]],\"start1\":20821,\"start2\":20821,\"length1\":16,\"length2\":264},{\"diffs\":[[0,\" app\"],[-1,\" — starting point for plugin\"],[0,\")\\\n- \"]],\"start1\":21212,\"start2\":21212,\"length1\":36,\"length2\":8},{\"diffs\":[[0,\"ce)\\\n\"],[-1,\"- Open Sim Relay (OpenSR) — architectural reference for plugin/relay pattern\\\n\"],[0,\"- DR\"]],\"start1\":21260,\"start2\":21260,\"length1\":85,\"length2\":8},{\"diffs\":[[0,\"tion\"],[-1,\", \\\"Project Cars 1\\\" protocol variant)\\\n- SimHub (reference for multi-game telemetry integration\"],[0,\")\\\n- \"]],\"start1\":21385,\"start2\":21385,\"length1\":101,\"length2\":8},{\"diffs\":[[0,\"ported |\"],[1,\"\\\n| Plausibility check coverage | All shared fields validated |\"],[0,\"\\\n\\\n---\\\n\\\n#\"]],\"start1\":21723,\"start2\":21723,\"length1\":16,\"length2\":78},{\"diffs\":[[0,\") | \"],[-1,\"Lua Plugin → UDP | **Phase 1.1 —\"],[1,\"KSUDP :9996 + Lua Plugin :5005 | **KSUDP can start now, plugin needs\"],[0,\" bug fix\"],[-1,\" needed\"],[0,\"** |\"]],\"start1\":21914,\"start2\":21914,\"length1\":55,\"length2\":84},{\"diffs\":[[0,\".0 | Native UDP \"],[1,\":5015 \"],[0,\"| Phase 1 |\\\n| 3 \"]],\"start1\":22017,\"start2\":22017,\"length1\":32,\"length2\":38},{\"diffs\":[[0,\"ive UDP \"],[1,\":5006 \"],[0,\"| Phase \"]],\"start1\":22081,\"start2\":22081,\"length1\":16,\"length2\":22},{\"diffs\":[[0,\"6-03\"],[-1,\" — debugging blank window issue, updated with confirmed root cause and log analysis\"],[0,\"*\\\n\\\n#\"]],\"start1\":22406,\"start2\":22406,\"length1\":91,\"length2\":8},{\"diffs\":[[0,\"ry\\\n\\\n\"],[-1,\"The window lifecycle:\\\n1. CSP reads `manifest.ini` and creates window frames from `[WINDOW_...]` sections\\\n2. For each window, CSP calls the `FUNCTION_MAIN` callback (e.g., `script.windowMain(dt)`) every frame\\\n3. If the callback function doesn't exist (because the script crashed at load time), the window frame appears but is **completely blank**\\\n\\\n### 13.2 CONFIRMED BUG: `require(\\\"socket.udp\\\")` — Wrong Module Path\\\n\\\n**Root cause of the blank window AND no UDP packets.**\\\n\\\nOur code (in `src/telemetry.lua`):\\\n```lua\\\nlocal socket = require(\\\"socket.udp\\\")   -- ❌ WRONG\\\nlocal udp = socket()\\\n```\\\n\\\n**`socket.udp` is NOT a LuaSocket module.** It is a *function* on the socket table. In CSP's bundled LuaSocket:\\\n\\\n| What we wrote | What it tries to find | Does it exist? |\\\n|---|---|---|\\\n| `require(\\\"socket.udp\\\")` | A Lua file at `socket/udp.lua` or C module `socket.udp` | **NO** — no such file exists |\\\n\\\n**CSP's bundled LuaSocket files** (at `extension/internal/lua-shared/`):\\\n```\\\nsocket.lua              ← main module (does require(\\\"socket.core\\\"))\\\nsocket/ftp.lua, headers.lua, http.lua, mbox.lua, smtp.lua, tp.lua, url.lua\\\n```\\\n\\\n**No `socket/udp.lua` file exists.** UDP is built into the native C module `socket.core`.\\\n\\\n**The correct pattern** (used by all CSP built-in scripts):\\\n```lua\\\nlocal socket = require(\\\"socket\\\")       -- loads socket.lua → socket.core (native C)\\\nlocal udp = socket.udp()               -- creates UDP socket object\\\n```\\\n\\\n**Evidence from AC log**: CSP creates the taskbar entry for our app (`IMGUI_LUA_ac-telemetry-plugin_main_ICON` on line 484 of log.txt), proving CSP sees the app. But the script silently crashes at `require(\\\"socket.udp\\\")` → no callbacks registered → blank window + no UDP packets.\\\n\\\n**Required fix** in `src/telemetry.lua`:\\\n```lua\\\nlocal socket = require(\\\"socket\\\")\\\nlocal udp = socket.udp()\\\n```\"],[1,\"### 13.2 CONFIRMED BUG: `require(\\\"socket.udp\\\")` — Wrong Module Path\\\n\\\n**Root cause of the blank window AND no UDP packets.**\\\n\\\n```lua\\\n-- ❌ CURRENT (broken):\\\nlocal socket = require(\\\"socket.udp\\\")   -- module doesn't exist\\\nlocal udp = socket()\\\n\\\n-- ✅ CORRECT:\\\nlocal socket = require(\\\"socket\\\")       -- loads socket.lua → socket.core (native C)\\\nlocal udp = socket.udp()               -- creates UDP socket from the table\\\n```\\\n\\\n**Required fix** in `src/telemetry.lua`.\"],[0,\"\\\n\\\n##\"]],\"start1\":22748,\"start2\":22748,\"length1\":1832,\"length2\":467},{\"diffs\":[[0,\"ng\\\n\\\n\"],[-1,\"#### \"],[1,\"**\"],[0,\"CSP \"],[-1,\"Log Files\"],[1,\"log location\"],[0,\" (Li\"]],\"start1\":23260,\"start2\":23260,\"length1\":26,\"length2\":26},{\"diffs\":[[0,\"ton)\"],[-1,\"\\\n\\\nLogs are written to the **Proton prefix**, not the game installation directory:\\\n\"],[1,\":**\"],[0,\"\\\n```\"]],\"start1\":23293,\"start2\":23293,\"length1\":90,\"length2\":11},{\"diffs\":[[0,\" log\"],[-1,\" (text, readable)\"],[0,\"\\\n├──\"]],\"start1\":23470,\"start2\":23470,\"length1\":25,\"length2\":8},{\"diffs\":[[0,\"F-16\"],[-1,\", not easily grepable\"],[0,\")\\\n├─\"]],\"start1\":23539,\"start2\":23539,\"length1\":29,\"length2\":8},{\"diffs\":[[0,\"\\\n\\\n**\"],[-1,\"Log evidence from the session:**\\\n- Line 484: `WARNING: TaskBarIcon :: COULD NOT FIND TEXTURES FOR IMGUI_LUA_ac-telemetry-plugin_main_ICON` — CSP sees our app but can't load the icon (Proton texture loading issue)\\\n- Lines 306-329: **ALL app icons fail to load** — not just ours, even built-in ones (AUDIO, CHAT, TELEMETRY, etc.). This is a Proton-wide texture loading issue, not specific to our plugin.\\\n- Line 442: `RemoteTelemetryUDP :: creating socket on port 9996` — AC's built-in UDP works under Proton (good sign for our LuaSocket fix)\\\n- **No Lua error messages** in `log.txt` — Lua errors go to `custom_shaders_patch.log` (binary) or the Lua Debug App\\\n\\\n#### In-Game Debugging Methods\\\n\\\n| Method | How to use | Where output goes |\\\n|---|---|---|\\\n|\"],[1,\"In-game debugging:**\\\n-\"],[0,\" **L\"]],\"start1\":23658,\"start2\":23658,\"length1\":757,\"length2\":30},{\"diffs\":[[0,\"p** \"],[-1,\"(best) | O\"],[1,\"— o\"],[0,\"pen \"]],\"start1\":23699,\"start2\":23699,\"length1\":18,\"length2\":11},{\"diffs\":[[0,\"en from \"],[-1,\"in-game \"],[0,\"app laun\"]],\"start1\":23707,\"start2\":23707,\"length1\":24,\"length2\":16},{\"diffs\":[[0,\"n). \"],[-1,\"Already enabled: `APP_LUA_DEBUG=1` in `extension/config/gui.ini` | Shows live\"],[1,\"Shows\"],[0,\" `ac\"]],\"start1\":23745,\"start2\":23745,\"length1\":85,\"length2\":13},{\"diffs\":[[0,\"` values\"],[-1,\",\"],[1,\" and\"],[0,\" `ac.log\"]],\"start1\":23766,\"start2\":23766,\"length1\":17,\"length2\":20},{\"diffs\":[[0,\"()` \"],[-1,\"messages, performance data |\\\n| **`ac.log(message)`** | Add to any Lua script |\"],[1,\"output live.\\\n- **`ac.log(message)`** — writes to\"],[0,\" CSP\"]],\"start1\":23786,\"start2\":23786,\"length1\":86,\"length2\":56},{\"diffs\":[[0,\"CSP log \"],[-1,\"file \"],[0,\"+ Lua De\"]],\"start1\":23839,\"start2\":23839,\"length1\":21,\"length2\":16},{\"diffs\":[[0,\" App\"],[-1,\" log |\\\n|\"],[1,\"\\\n-\"],[0,\" **`\"]],\"start1\":23858,\"start2\":23858,\"length1\":16,\"length2\":10},{\"diffs\":[[0,\"lue)`** \"],[-1,\"| Track\"],[1,\"—\"],[0,\" live va\"]],\"start1\":23884,\"start2\":23884,\"length1\":23,\"length2\":17},{\"diffs\":[[0,\"e values\"],[-1,\",\"],[1,\" with\"],[0,\" optiona\"]],\"start1\":23897,\"start2\":23897,\"length1\":17,\"length2\":21},{\"diffs\":[[0,\"onal\"],[-1,\"ly with\"],[0,\" graphs \"],[-1,\"|\"],[1,\"in\"],[0,\" Lua\"]],\"start1\":23915,\"start2\":23915,\"length1\":24,\"length2\":18},{\"diffs\":[[0,\" App\"],[-1,\" (in-game) |\\\n|\"],[1,\"\\\n-\"],[0,\" **`\"]],\"start1\":23939,\"start2\":23939,\"length1\":22,\"length2\":10},{\"diffs\":[[0,\"`** \"],[-1,\"| P\"],[1,\"— p\"],[0,\"rint\"],[1,\"s\"],[0,\" to AC \"],[-1,\"console | I\"],[1,\"i\"],[0,\"n-ga\"]],\"start1\":23968,\"start2\":23968,\"length1\":33,\"length2\":24},{\"diffs\":[[0,\"le (\"],[-1,\"toggle with \"],[0,\"back\"]],\"start1\":24000,\"start2\":24000,\"length1\":20,\"length2\":8},{\"diffs\":[[0,\"key)\"],[-1,\" |\\\n| **`ac.getLastError()`** | Check if script has thrown errors | Returns string or nil |\\\n| **`ac.warn()`, `ac.error()`** | Log warnings/errors | CSP log + Lua Debug App |\\\n\\\n**Recommended: Add these calls to our plugin for debugging:**\\\n```lua\\\n-- In main.lua, at the top:\\\nac.log(\\\"[ACTelemetry] Script loading...\\\")\\\n\\\n-- In telemetry.lua, after socket creation:\\\nlocal socket = require(\\\"socket\\\")\\\nac.log(\\\"[ACTelemetry] LuaSocket loaded successfully\\\")\\\nlocal udp = socket.udp()\\\nac.log(\\\"[ACTelemetry] UDP socket created\\\")\\\n\\\n-- In telemetry.update(), after successful send:\\\nac.debug(\\\"tx.rate\\\", telemetry.getSendRate(), 0, 120, 60)\\\nac.debug(\\\"tx.packets\\\", telemetry.getPacketCount())\\\nac.debug(\\\"tx.errors\\\", telemetry.getErrorCount())\\\n```\\\n\\\n#### Proton Icon Loading Issue\\\n\\\nThe log shows **every single app icon** fails to load under Proton:\\\n```\\\nWARNING: TaskBarIcon :: COULD NOT FIND TEXTURES FOR AUDIO_ICON\\\nWARNING: TaskBarIcon :: COULD NOT FIND TEXTURES FOR CHAT_ICON\\\nWARNING: TaskBarIcon :: COULD NOT FIND TEXTURES FOR TELEMETRY_ICON\\\n... (30+ icons all fail)\\\n```\\\n\\\nThis is a known Proton/DXVK issue with texture loading from the AC app system. The apps still work — only the sidebar icons are missing. The app window itself should render correctly (ImGui draws are separate from icon textures).\\\n\\\n### 13.4 Should We Have Two Variants (App + Extension)?\\\n\\\n**No. One variant, deployed as an app.**\\\n\\\nReasons against maintaining two variants:\\\n\\\n1. **Double-loading risk**: If both `apps/lua/` and `extension/lua/` copies exist, CSP loads both → **double UDP transmission** (120 packets/sec instead of 60)\\\n\\\n2. **Code divergence**: The app variant has different `manifest.ini` and `main.lua`. Maintaining two sets of files for the same plugin creates confusion about which is \\\"current\\\"\\\n\\\n3. **Extension is misplaced**: The `extension/lua/` copy sits at the root level, not under a recognized subsystem directory. CSP might not even load it correctly as an extension.\\\n\\\n4. **No benefit to the extension variant**: The app variant with `LAZY = FULL` already gives you the best of both worlds:\\\n   - **When the app window is closed**: The script unloads completely (same as not having it)\\\n   - **When the app window is open**: Telemetry runs with visual feedback\\\n   - If you want always-on telemetry, just leave the app window open (it's tiny: 160×30 px)\\\n\\\n5. **Future settings panel**: Only the app variant supports `FUNCTION_SETTINGS` for an in-game configuration UI (host/port). The extension variant has no UI at all.\\\n\\\n**Decision**: Maintain only the app variant in git. Delete the extension/lua copy. Deploy from project source to `apps/lua/` only.\\\n\\\n### 13.5 Proton Rendering Considerations\\\n\\\nThe blank windows are **confirmed to be script crash bugs**, not Proton rendering issues. However, Proton/DXVK can cause secondary rendering issues:\\\n\\\n- **`NO_BACKGROUND` flag**: Creates a transparent window. Under DXVK/WineD3D, transparency compositing may behave differently than Windows D3D11. If the text renders but is invisible, try removing `NO_BACKGROUND,NO_TITLE_BAR` flags.\\\n- **`FADING` flag**: Windows fade when not focused. Proton's focus detection via Wine might not work correctly, causing windows to remain fully transparent.\\\n- **App icons don't load**: Confirmed from log — all app icons fail under Proton. Apps still work, only sidebar icons are missing.\\\n- **ImGui rendering itself works**: AC's built-in apps (CspDebug, AppShelf) use the same ImGui system and function correctly.\\\n\\\n**For Phase 1, rendering is not the primary issue.** Once the LuaSocket bug is fixed, the window should show the \\\"AC Telemetry ●\\\" text. If it's still blank after the fix:\\\n1. Remove `NO_BACKGROUND,NO_TITLE_BAR` flags temporarily\\\n2. Remove `FADING` flag\\\n3. Try a larger window size to confirm rendering is working\\\n\\\n### 13.6 UDP Configuration: Current and Future Options\\\n\\\n#### Current: `config.lua` (hardcoded)\\\n\\\n```lua\\\nlocal config = {\\\n    target_host = \\\"127.0.0.1\\\",\\\n    target_port = 5005,\\\n    send_interval = 1 / 60,\\\n}\\\nreturn config\\\n```\\\n\\\n**Pros**: Simple, no overhead, easy to understand\\\n**Cons**: Requires file editing + AC restart to change; no validation\\\n\\\n#### Option A: In-Game Settings Panel (Recommended for Phase 2)\\\n\\\nUse `[SETTINGS]` in manifest.ini to add a gear icon that opens a settings window:\\\n\\\n```lua\\\nfunction script.windowSettings(dt)\\\n    ui.text(\\\"UDP Target\\\")\\\n    ui.inputText(\\\"Host\\\", storage.targetHost)\\\n    ui.inputText(\\\"Port\\\", storage.targetPort)\\\n    if ui.button(\\\"Apply\\\") then\\\n        reconnect()\\\n    end\\\nend\\\n```\\\n\\\nPersist with `ac.storage()` — survives app restarts within a session.\\\n\\\n#### Option B: `ac.storage()` + config.lua defaults (Recommended for Phase 1 fix)\\\n\\\nLoad defaults from `config.lua`, allow override from `ac.storage()`:\\\n\\\n```lua\\\nlocal defaults = require(\\\"config\\\")\\\nlocal storage = ac.storage({\\\n    target_host = defaults.target_host,\\\n    target_port = defaults.target_port,\\\n})\\\n```\\\n\\\nThis gives you file-based defaults with runtime persistence. No UI needed.\\\n\\\n#### For cross-machine deployment (analysis machine on different IP)\\\n\\\nChange `target_host` from `\\\"127.0.0.1\\\"` to the analysis machine's LAN IP (e.g., `\\\"192.168.1.100\\\"`). The UDP datagram goes through Proton's network stack → Linux kernel → LAN → analysis machine. No special configuration needed — standard UDP networking\"],[1,\"\\\n\\\n### 13.4 One Variant: App Deployment Only\\\n\\\n**Maintain only the `apps/lua/` app variant.** Delete the `extension/lua/` copy. Reasons:\\\n1. Double-loading risk (120 pkt/s instead of 60)\\\n2. App variant supports settings UI (`FUNCTION_SETTINGS`)\\\n3. `LAZY = FULL` gives on-demand loading\\\n4. Extension copy is at wrong path (not under a subsystem directory)\\\n\\\n### 13.5 Proton Rendering\\\n\\\n- **All app icons fail to load** under Proton — confirmed from log. Apps still work, only sidebar icons missing.\\\n- ImGui rendering itself works (built-in apps function correctly).\\\n- If window is still blank after LuaSocket fix, remove `NO_BACKGROUND,NO_TITLE_BAR,FADING` flags temporarily.\\\n\\\n### 13.6 UDP Configuration\\\n\\\nCurrent: `config.lua` with hardcoded `127.0.0.1:5005`. For cross-machine, change `target_host` to LAN IP. Future: settings panel via `FUNCTION_SETTINGS`\"],[0,\".\\\n\\\n-\"]],\"start1\":24019,\"start2\":24019,\"length1\":5285,\"length2\":859},{\"diffs\":[[0,\"s |\\\n\"],[-1,\"| `ui.beginTransparentWindow()` / `ui.endTransparentWindow()` | Overlay debug window | `drift-challenge/debug_display.lua` |\\\n\"],[0,\"| Di\"]],\"start1\":25381,\"start2\":25381,\"length1\":133,\"length2\":8},{\"diffs\":[[0,\" |\\\n\\\n\"],[-1,\"### Key UI Functions Available in CSP Lua\\\n\\\n**Text rendering:**\\\n- `ui.text(str)` — simple text\\\n- `ui.textColored(str, rgbm(r,g,b,a))` — colored text\\\n- `ui.textAligned(str, align, size)` — aligned text\\\n- `ui.dwriteDrawText(str, size, pos, color)` — DirectWrite text\\\n\\\n**Drawing primitives:**\\\n- `ui.drawRectFilled(pos1, pos2, color, rounding)` — filled rectangle\\\n- `ui.drawRect(pos1, pos2, color, rounding, thickness)` — rectangle outline\\\n- `ui.drawCircleFilled(center, radius, color, segments)` — filled circle\\\n- `ui.drawLine(from, to, color, width)` — line\\\n- `ui.pathArcTo()`, `ui.pathStroke()` — arcs and paths\\\n\\\n**Layout:**\\\n- `ui.sameLine()`, `ui.offsetCursorX/Y()` — cursor positioning\\\n- `ui.availableSpaceX()`, `ui.windowSize()` — size queries\\\n- `ui.pushFont()`, `ui.popFont()` — font switching\\\n- `ui.separator()` — horizontal line\\\n\\\n**Interactive:**\\\n- `ui.button(label, size)` — clickable button\\\n- `ui.slider(propertyName, value, min, max)` — slider control\\\n- `ui.checkbox(label, value)` — toggle checkbox\\\n\\\n---\\\n\\\n## 15. CSP Built-in Lua Scripts Reference\\\n\\\nCSP ships with 96+ Lua scripts in `extension/lua/` organized by category:\\\n\\\n| Category | Path | Description | UI Pattern |\\\n|----------|------|-------------|------------|\\\n| Android Auto | `cars/android_auto/` | Full infotainment system with apps | `ui.drawImage()`, `ui.childWindow()`, touchscreen |\\\n| Drift Challenge | `new-modes/drift-challenge/` | Drift scoring mode with HUD | `ui.drawRectFilled()`, `ui.dwriteDrawText()` |\\\n| Traffic Tool | `tools/csp-traffic-tool/` | AI traffic scenario editor | `ui.header()`, `ui.slider()`, `ui.button()` |\\\n| Fireworks | `fireworks/holidays/` | Holiday themed fireworks | No UI, pure rendering |\\\n| PP Filters | `pp-filters/` | Post-processing effects | `ui.windowSize()` + shader uniforms |\\\n| Camera Systems | `chaser-camera/`, `cockpit-camera/` | Camera behavior scripts | No UI |\\\n| FFB Processing | `ffb-postprocess/` | Force feedback scripts | No UI |\\\n\\\n### CSP Internal Lua Apps (in `extension/internal/lua-apps/`)\\\n\\\nCSP's own built-in apps that use the same `[WINDOW_...]` pattern:\\\n\\\n| App | Purpose | LAZY | Windows |\\\n|-----|---------|------|---------|\\\n| CspDebug | Debug lights, physics outlines, VAO | FULL | 2 (main + settings) |\\\n| AppShelf | App launcher/organizer | FULL | 1 |\\\n| Mumble | Voice chat positioning | 0 (always on) | 1 |\\\n\\\n---\\\n\\\n*Last updated: 2026-06-03*\\\n*Source: Research conversation on sim racing telemetry APIs and system design*\\\n*Phase 1.1 completed: 2026-06-01 — ac-telemetry-plugin (3900d2a), rusty-telemetry (30543fd)*\\\n*Blank window debugging: 2026-06-03 — confirmed LuaSocket bug, CSP log analysis, deployment paths documented*\\\n*Bug fix needed: `require(\\\"socket.udp\\\")` → `require(\\\"socket\\\")` + `socket.udp()`*\\\n*Cleanup needed: Delete `extension/lua/ac-telemetry-plugin/`, merge app variant into git\"],[1,\"---\\\n\\\n*Last updated: 2026-06-03*\\\n*Source: Research conversation on sim racing telemetry APIs and system design*\\\n*Phase 1.1 completed: 2026-06-01 — ac-telemetry-plugin (3900d2a), rusty-telemetry (30543fd)*\\\n*Architecture update: 2026-06-03 — dual-source telemetry (port 9996 + 5005), KSUDP parser added to plan\"],[0,\"*\"]],\"start1\":25509,\"start2\":25509,\"length1\":2825,\"length2\":312}]"
metadata_diff: {"new":{},"deleted":[]}
encryption_cipher_text: 
encryption_applied: 0
updated_time: 2026-06-03T07:37:53.010Z
created_time: 2026-06-03T07:37:53.010Z
type_: 13