fix(dynamics): overhaul steer-balance algo to trust slip angle over yaw#60
Open
fix(dynamics): overhaul steer-balance algo to trust slip angle over yaw#60
Conversation
Why: the analyst route previously bypassed Mastra (direct Gemini/OpenAI
fetch) so agent tools could never fire. F1 tuning suggestions had no
grounding in what fast drivers actually run.
Route migration:
- POST /api/laps/:id/analyse now calls lapAnalystAgent.generate via the
dev/prod agent split in server/ai/agents.ts (tree-shakes DuckDB out of
the prod binary, same pattern as the chat flow)
- Keystore → env bridge for Gemini/OpenAI/local providers
- generate(..., { maxSteps: 5 }) so tool rounds are allowed
F1 setup comparison tool:
- mastra/tools/f1-setup-compare.ts exposes compare-f1-setup-to-catalog:
input { lapId }, output { currentSetup, references[5] with per-field
delta } sourced from shared/tunes/f1-25/f1laps/
- server/ai/f1-setup-catalog.ts bundles all 24 track setup JSONs at
import time so the Mastra dev bundler doesn't readFileSync-fail
- normalizePacketSetup remaps packet keys (onThrottle) onto the
catalog shape (diffOnThrottle) so diffs line up
- Falls back to scanning lap.telemetry for f1.setup when the carSetup
column is null on older laps
- getLapById now returns carSetup (was SELECTed but dropped from the
return object); hasTune accounts for F1 carSetup presence
- Lap Analyst + Lap Chat agents register the tool; F1 adapter prompt
instructs the model to call it before filling in tuning[]
Shared analyst output schema:
- server/ai/schemas.ts extracts AnalystOutputSchema (verdict, pace,
handling, corners, technique, setup, tuning) as the single source
of truth
- FM, F1, ACC, and AC Evo adapter prompts render their JSON shape
via renderAnalystSchemaForPrompt() — ACC/AC-Evo previously emitted
free-form prose
Deterministic evaluators:
- 6 scorers in mastra/evals/scorers/: output-shape, corner-coverage,
numeric-grounding, unit-consistency, compare-directionality,
chat-freeform-shape
- Eval-only Gemini 3 Flash agent factory (mastra/evals/eval-agents.ts)
so eval runs are reproducible regardless of user settings
- test/ai-quality.test.ts gate + test/ai-fixtures scaffold
(3 lap fixtures + 1 compare pair; packet zips provided by the
contributor via laps:export)
- scripts/ai-baseline.ts for SHA-tagged score snapshots
- ai-quality CI job in build-test.yml runs bun run test:ai
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Continues the first mastra-backed analyst commit: tighter prompt discipline, a unified setup schema across games, F1 tool-call wiring, and a live status UI in the chat panels. Prompt & schema: - Unified AnalystOutputSchema (single setup[]) so TuneBars render across FM, F1, ACC, AC Evo - DISCIPLINE block in lap-analyst: step caps, ranked reference citations, data-cited symptoms, corner whitelist / Tn fallback - Session Type from F1 packets drives ERS rules (quali vs race) - Per-adapter THERMAL REFERENCE blocks (tyre/brake bands, health) - Setup coverage bumped (F1 8-14, others 6-12) with category coverage Streaming chat protocol: - server/ai/chat-stream.ts NDJSON emitter from agent.fullStream - Bun idle timeout bumped 120 -> 255s for slow local models - client/src/lib/chat-stream.ts line parser - AiPanel + CompareAiPanel show thinking / tool-call / generating status chips and input/output token footer - 15s heartbeat ping keeps sockets alive pre-first-token F1 setup tool: - carSetup fallback scan of lap.telemetry + key normalisation - delta coerced to Record<string,number> for zod outputSchema UI: - SetupSection modal (body-only scroll) shared by AnalysisDisplay and AiPanel - TuneBar fix for negative-value fields (camber/toe) - AnalyseLapHeader hides Forza tune picker for F1 25 - CompareAiPanel input-comparison segments clickable - SessionsPage 'Compare 2 laps' button next to Delete Defaults: - Gemini + gemini-flash-latest across settings and fallbacks TS: - zod v4 SafeParseReturnType -> ReturnType<safeParse> - AI SDK v4 usage via defensive accessor - CompareAiPanel InputsModal receives trackSegments/onJumpToFrac (earlier edit was in the wrong scope) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Route local (LM Studio) through @ai-sdk/openai with createOpenAI + explicit
.chat(id) so requests hit /v1/chat/completions, not /v1/responses.
- Disable reasoningEffort ("none") for local to stop Gemma-style reasoning
models from burning the 8k output budget on hidden thinking.
- Switch response_format to strict json_schema (OpenAI Structured Outputs)
so the decoder is grammar-constrained — no mid-string truncation / bad
chars. Same schema also passed as google.responseSchema for Gemini.
- Don't cache invalid/truncated model output; drop stale empty caches on
read so retries aren't poisoned by pre-validation runs.
- Client: safeParseAnalysis logs a window around the failing byte on JSON
parse errors; fix ChatStreamEvent narrowing via as-unknown casts.
- Add guides/local-ai.md covering the LM Studio setup flow.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Analyse: replace plain JSON with heartbeat NDJSON (ping every 200s, single result at end) so slow local models don't hit Bun's 255s idleTimeout. Client handles ping / result / error only — no intermediate status UI. - F1 25 setup ranges: correct the prompt and client TuneBar bounds to match the game's actual slider limits (Diff 10–100, ARB 1–41, Ride Height 20–50, rear tyre pressure 20.0–26.5 psi, toe 0–0.10/0.40). - Drop all DRS mentions from the F1 analyst: prompt category guidelines, rules block, and the DrsActive context builder. Zone data isn't reliable enough to reason from, and raw activation counts aren't actionable. - Metric labels: humanise snake_case/camelCase at render time and tell the model to emit Title Case with spaces. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Gate yaw signal on latG floor only; slip angle (lateral-only) now always contributes - When signals conflict, use slip angle alone (yaw unreliable at high speed, Ay/V → 0) - When slip angles are balanced (|uSlip| < 0.15), ignore yaw spikes from diff torque / wheelspin - Fixes: neutral wrongly shown at 0.23g, oversteer wrongly shown at high speed, oscillation under full-throttle throttle - Expose uSlip, uYaw, signalsAgree in SteerBalance for tooltip signal breakdown viz - Steering overlay now uses configured lock (consistent with data panel) - Throttle/brake colors now trigger at any non-zero value - Add 9-scenario snapshot test suite (test/steer-balance.test.ts) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Slip angle is now always authoritative. Yaw can push balance further from zero when it strongly agrees, but is discarded when the blend would move the result closer to zero than slip alone. Validated against F1 lap 391 (28975 pkts) and ACC lap 461 (5538 pkts) — fixes ACC pkt-360-style cases where weak yaw was washing out clear understeer below classify threshold. Adds 4 new snapshot tests covering amplification, dilution suppression, amplified oversteer, and conflict resolution. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
|uSlip| < 0.15(tires reporting balanced), yaw spikes from rear wheelspin/diff torque are ignoredBugs fixed
Test plan
bun run test test/steer-balance.test.ts— 9 scenario snapshot tests all pass🤖 Generated with Claude Code