Catalog FSD disengagements from Tesla Dashcam clips with a per-firmware regression dashboard.
Project description
fsd-disengagement-studio
Catalog and classify FSD disengagements from your saved Tesla Dashcam clips. Newly viable in May 2026 because firmware 2025.44.25+ embeds frame-perfect SEI metadata (including AutopilotState) in saved clips, and Tesla published the proto schema in teslamotors/dashcam (Dec 4, 2025).
Install
# From PyPI (once published)
pip install fsd-disengagement-studio # core CLI + dashboard
pip install 'fsd-disengagement-studio[sei]' # +protobuf/pandas to parse Dashcam SEI
pip install 'fsd-disengagement-studio[dev]' # +pytest, ruff, httpx
Then try the dashboard with no Tesla and no API keys:
fsd-studio demo # seeds + serves at http://127.0.0.1:8000/
The wave-3 spec at ../ideas/v2/wave3/fsd_disengagement_studio.md walks through the full design rationale.
What it does
For each saved Dashcam event:
- Parse the SEI metadata stream to find frames where
AutopilotStatetransitions fromSELF_DRIVING/AUTOSTEERtoNONEwhilevehicle_speed_mps > 1. (The speed gate filters out parking-lot manual-takeover events.) - For each disengagement, sample keyframes from front + back cams in a ±5s window around the transition.
- Classify the cause via VLM (gpt-4o-mini default, ~$0.0008/event) into a structured
DisengagementCauseenum (phantom_brake, lane_confusion, occluded_sign, hov_decision, driver_pre_empt, traffic_light_hesitation, construction_zone, sun_glare, weather, unclear, other). - Persist to SQLite with firmware version + GPS + clip path + confidence.
- Serve a FastAPI + HTMX dashboard showing per-firmware regression: which cause categories are improving, which are worsening across firmware versions.
Composability with the v3 stack
This project is the second consumer of tesla-clip-tools (the first being sentrytriage). The library provides:
tesla_clip_tools.sources.tesla.TeslaSource— walks the canonical TeslaCam folder layouttesla_clip_tools.sampler.sample_keyframes— frame extraction via imageio (bundled ffmpeg)tesla_clip_tools.vlm.openai.OpenAIBackend— generic VLM call parameterised onresponse_formattesla_clip_tools.sei.iter_sei_payloads/sei_dataframe— the SEI byte-walker
This repo adds: the FSD-specific schema, the transition detector, the per-firmware regression dashboard, and the disengagement-cause classifier prompt.
Status — v0.4 alpha
What works today:
- Path B auto-saves the dashcam clip (added v0.4):
fsd-studio listen --auto-savePOSTs to Tesla'smanual_save_dashcam_eventFleet API the moment a disengagement is queued, so Path C is guaranteed to find the matching .mp4 inSavedClips/later. Per-vehicle 30s cooldown stops a re-broadcast burst from spamming the API; Fleet API errors are logged but never crash the listen loop.--mock-fleetruns a realistic dry-run without contacting Tesla. The dashboard/pendingpage surfaces the running auto-save counter ("Auto-saves issued: N"). - All three paths collaborate (v0.3): Path A (manual save), Path B (real-time trigger), and Path C (bulk backfill) compose as one system.
fsd-studio listenqueues real-time disengagements to a JSONL logfsd-studio backfill --pending-log <path>reads the log, matches each newly-classified DisengagementEvent to a queued record by±300s(configurable) timestamp window, marks the record with the matched event's ID, and atomically rewrites the log- Dashboard
GET /pending?unmatched_only=true(default) shows queued records still waiting for a clip;GET /pending/stale?stale_after_hours=24surfaces missing-clip warnings (Path B fired but no clip was ever saved)
- Path B real-time trigger (v0.2):
fsd-studio listen --ha-url ... --ha-token ...subscribes to HA'stesla_fleet_telemetryevent bus, runs a statefulDisengagementDetectorwith cooldown + low-speed gate, appends each detected transition to a JSONL log. Detector handles both explicitautopilot_statetransitions and inferred-from-self_driving_milesplateaus. Fully testable withMockTelemetryStream. - Pure-Python
find_disengagements()finds engaged → disengaged transitions in any sequence ofFrameTelemetrytuples (testable without a Tesla via mocked sequences) DisengagementClassificationPydantic schema with 11 cause categoriesDisengagementEventSQLModel +Storewith upsert, list, and per-firmware aggregationfsd-studioCLI withclassify,backfill,servecommandsfsd-studio servestarts a FastAPI + HTMX + ECharts dashboard atlocalhost:8000- Test suite covers the schema, transition detector, store aggregation, and FastAPI routes (TestClient — no real server needed)
What needs a real Tesla + the [sei] extra:
iter_disengagements_from_clip()— callssei_dataframe(mp4_path)which requires the[sei]extra (pip install 'fsd-disengagement-studio[sei]') plus a one-timeprotoc --python_out=. dashcam.prototo generatedashcam_pb2.pyintesla-clip-tools.- The
backfillCLI command — needs real saved Dashcam clips with SEI to do meaningful work.
What's coming in v0.5+ (per wave-3 spec):
- Per-route map (Leaflet) showing where disengagements cluster
- Per-cause Sankey across firmware versions
- Regression alerts (push when a cause spikes vs trailing N-week baseline)
- Reviewer-feedback fine-tuning (thumbs-up/down on each event tunes a small classifier on top of the VLM embeddings)
Path B auto-save (v0.4) -- usage
export TESLA_FLEET_ACCESS_TOKEN=... # from your Fleet API OAuth flow
export TESLA_VEHICLE_ID=... # the vehicle id (or VIN) to target
export HA_URL=http://homeassistant.local:8123
export HA_LONG_LIVED_TOKEN=...
# Auto-save is on by default; the matching clip lands in SavedClips/.
uv run fsd-studio listen
# Dry-run (no real Fleet API call):
uv run fsd-studio listen --mock-fleet --vehicle-id rehearsal
# Disable entirely (v0.3 behavior):
uv run fsd-studio listen --no-auto-save
The dashboard's /pending page renders an "Auto-saves issued: N" stat panel
fed by a sidecar <pending-log>.autosave-stats.json that the listener writes
after each event. JSON consumers can hit GET /api/autosave-stats.
Quick start (without a Tesla)
git clone https://github.com/Raymondriter/fsd-disengagement-studio.git
cd fsd-disengagement-studio
uv sync --extra dev
uv run pytest # 15+ tests, no fixtures needed
uv run fsd-studio serve --port 8000 # empty dashboard at http://localhost:8000
Quick start (with a Tesla)
uv sync --extra sei --extra dev
# Generate the SEI proto module (one-time):
cd ../tesla-clip-tools/src/tesla_clip_tools
protoc --python_out=. dashcam.proto
cd -
export OPENAI_API_KEY=sk-...
uv run fsd-studio backfill /path/to/SavedClips --firmware 2026.14.3
uv run fsd-studio serve
Important caveats
- SEI is only in Dashcam clips (SavedClips, RecentClips), not Sentry events. The wave-3 SEI deep-dive confirmed this. So this tool reads from
SavedClips/(andRecentClips/), NOTSentryClips/. TheSavedClips/folder layout is identical toSentryClips/—tesla_clip_tools.sources.tesla.TeslaSourcehandles either. - Path B (Fleet Telemetry triggered save) is fragile in v0.1. Tesla hasn't shipped a first-class autopilot-state transition signal; it has to be inferred from
SelfDrivingMilesSinceResetrate-drop. v0.1 ships only Path A (manually-saved clips) + Path C (bulk historical backfill). Path B is v0.2 opt-in. - Legal risk for individual-owner use is low. Owner owns vehicle-generated data per Tesla's Privacy Notice + Fleet API Terms; teslafsdtracker.com and MATT3R K3Y have published cross-owner FSD-disengagement data since 2020 with no Tesla legal action; Tesla itself publishes aggregate data in its FSD Vehicle Safety Report. Single-VIN by default. Footer disclaimer in the dashboard reminds users not to generalize from n=1 in public posts.
Screenshots
The dashboard rendered against synthetic demo data (fsd-studio demo):
Changelog
See CHANGELOG.md. Versions follow Keep a Changelog and the project uses SemVer.
License
MIT. See LICENSE.
CI
GitHub Actions runs ruff + pytest on Python 3.12 and 3.13 against every push and PR. See .github/workflows/ci.yml. Until tesla-clip-tools is published to PyPI, the standalone CI strips the [tool.uv.sources] table and resolves it as a regular dependency; the workspace-level monorepo CI at C:\Dev\tesla\.github\workflows\ci.yml keeps using the path-editable sibling.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fsd_disengagement_studio-0.4.0.tar.gz.
File metadata
- Download URL: fsd_disengagement_studio-0.4.0.tar.gz
- Upload date:
- Size: 46.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.12 {"installer":{"name":"uv","version":"0.11.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
84ed01bc22fb223020a7fcf674244b6c5bb08a7980df79a92f222be0f243f4d6
|
|
| MD5 |
6b6638a91b9d36d22f8e4a261bdd4050
|
|
| BLAKE2b-256 |
593a35c41934c811e7c52339ab33471ca6863edbd9e54a6d46c84ff486d908e7
|
File details
Details for the file fsd_disengagement_studio-0.4.0-py3-none-any.whl.
File metadata
- Download URL: fsd_disengagement_studio-0.4.0-py3-none-any.whl
- Upload date:
- Size: 42.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.12 {"installer":{"name":"uv","version":"0.11.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
542c20b425277fd0f15a61facd77fd69c60a4cea766b7153927cc2f1638498d4
|
|
| MD5 |
218fa76ecf818b51ff8d2dc618f6b567
|
|
| BLAKE2b-256 |
8ad2223d5d662911ced1d7c2414764ecd55252540a93e73e85fbc296d100b5be
|