Imported from moto-platform/moto-safety-node (
AGENTS.md). Install upstream withnpx skills add moto-platform/moto-safety-node. Copyright stays with the author.
AGENTS.md — moto-platform rules for AI coding tools other than Claude Code
You are an AI coding tool (Codex, Gemini, DeepSeek, Cursor, Aider, Copilot, …) working on moto-platform, a motorcycle diagnostics / telemetry / ADAS platform with safety-relevant firmware. Claude Code is the project's reviewing and safety-gating tool; you work alongside it under the rules below (D-071). If a rule here conflicts with what the user asks, say so and ask before acting.
1. Every session
- Read
STATUS.mdat the workspace root (short): where the project is and what is next. If you are inside a single repo without the workspace, say so; the zone checker and the log folder live in the workspace. - For architecture facts read
moto-vehicle-defs/docs/ARCHITECTURE.mdandDECISIONS.md(only the parts you need;docs/README.mdindexes the large docs). Never guess an architecture fact and never re-decide a recorded decision. - Before editing a file, check its zone:
python3 scripts/check_zones.py <repo> --path <file>(workspace root). Red = do not edit. - Open your log entry (section 5) at the start, not at the end.
2. Edit zones (external-ai/zones.json, first matching rule wins)
-
red — Claude only. Do not edit, not even "small fixes". Describe the wanted change under For Claude in your log and continue with something else.
-
yellow — allowed; the PR needs a Claude review before the user merges it (write
needs-claude-reviewin the PR description). -
green — allowed; still log it.
-
All repos (checked first) — red:
.claude/*,AGENTS.md,GEMINI.md,CLAUDE.md,.github/*,external/moto-vehicle-defs,external/*,gen/*,*/gen/*,misra/* -
moto-workspace — red:
external-ai/log/TEMPLATE.md,external-ai/*,PLATFORM-RULES.md,manifest.yaml,scripts/*,setup.sh; yellow:STATUS.md,ISSUES.md; green:external-ai/log/*; everything else: yellow -
moto-vehicle-defs — red:
docs/DECISIONS.md,docs/ARCHITECTURE.md; yellow:docs/*; green:docs/ideas/*; everything else: red -
moto-safety-node — everything: red
-
moto-io-node — everything: red
-
moto-rt-core — red:
src/features/cornering/*,src/features/uds/*,src/features/heartbeat/*,src/features/vehicle_republish/*,src/features/*anomaly*,src/features/*boot*,src/services/alive*,src/services/snapshot*,src/services/can_*,src/services/mailbox*,src/app/*init*,cubemx/*,*.ld; everything else: yellow -
moto-connectivity-node — red:
src/HondaCANModule*,src/DiscoveryScan*,src/IsoTp*,src/TesterLatch*,src/CanCapture*,src/hal/Twai*,src/hal/RtcTesterLatchStore*,platformio.ini; yellow:src/Gps*; everything else: yellow -
moto-hil-bench — red:
*fault*; everything else: yellow -
moto-linux-node — red:
*ota*; everything else: yellow -
moto-server — red:
*ota*; yellow:*gps*,*position*; everything else: green -
moto-mobile — yellow:
lib/services/session_upload*,lib/services/upload_client*; everything else: green -
moto-mcp — everything: yellow
-
moto-ml — everything: green
-
Unlisted repos and paths: yellow. Where two globs match, the more specific one listed first in
external-ai/zones.jsonwins (e.g.docs/ideas/*beforedocs/*); the file also gives each rule's reason.
3. Hard rules that no path rule can catch
- No hand-written signal facts. CAN IDs, DIDs, signal names, scales, units, VSS paths and BLE layouts come only from the generated
gen/code or the definition files of moto-vehicle-defs. Never write such a literal in code; if a signal is missing, that is a red-zone request (/signal-changeby Claude). - Nothing new on the vehicle bus. No code that sends a CAN frame to the motorcycle outside the existing tester modules (red); no new UDS service, no write, clear, reset, unlock or reprogram, ever.
- MCU code (rt-core, safety-node, conn, hil-bench firmware): no heap (
malloc,new, STL containers that allocate) after init, no string/JSON/DBC parsing, no blocking calls in the tester loop, MISRA C:2012 style in C. - Privacy: never commit keys, passwords, real ride data, VINs, or GPS positions. Position may go only to the user's own servers (D-076, provisional); code that moves it is red (Claude only). The repos are public.
- Safety path: never make a safety decision (cornering, blind spot, immobilizer) depend on the Raspi, the phone, Wi-Fi, VSS or ML; ML may only tighten thresholds.
- No new dependency in firmware or in
pyproject.toml/pubspec.yamlwithout asking the user. - Do not weaken checks: no disabled tests, lowered coverage, new MISRA suppressions,
# noqaor// cppcheck-suppressto make CI green. Fix the code or log the problem. - No releases: no tags, no version bumps, no submodule pin changes, no
manifest.yamledits (D-036). Never merge a PR and never push tomain; the user merges.
4. Workflow
- Branch:
ext/<tool>-<topic>(e.g.ext/gemini-mobile-dashboard), in the repo you change. Nevermain, never aclaude/branch. One topic per branch. - Commits: Conventional Commits in English (
feat(mobile): ...) with the trailer lineAI-Agent: <tool>/<model>(e.g.AI-Agent: gemini-cli/gemini-2.5-pro). Claude finds your work withgit log --grep '^AI-Agent:'. - Before each commit: run the repo's checks (table below) and
python3 scripts/check_zones.py <repo>. Red → revert those paths. Commit and push only when the user asks. - PR (when the user asks): title in Conventional Commits form; description = summary, checks run with their result, the zone result,
needs-claude-reviewif any path is yellow, and the log file name. - Write code that reads like the surrounding code: same naming, comment density, test style. Add or update tests for what you change.
| Repo | Checks (run from the repo root) |
|---|---|
| moto-vehicle-defs | red: do not edit (docs only: no check) |
| moto-rt-core | cmake --preset host-tests && cmake --build --preset host-tests && ctest --preset host-tests (+ host-tsan preset for snapshot/alive/IMU); cppcheck as in .github/workflows/ci.yml |
| moto-connectivity-node | scripts/native_tests.sh (or pio test -e native); pio run -e esp32-s3-devkitc-1-mock; cppcheck as in CI |
| moto-mobile | flutter pub get && flutter analyze && flutter test (never commit a local pubspec.lock rewrite) |
| moto-server | uv sync && uv run pytest && uv run ruff check . && uv run ruff format --check . |
| other repos | the commands in that repo's README/CLAUDE.md "Build" section |
5. Session log (mandatory)
Every session writes one file: external-ai/log/YYYY-MM-DD-<tool>-<topic>.md in the workspace, copied from external-ai/log/TEMPLATE.md, with review: pending. Fill it while you work: what changed (repo, branch, files), why, checks and results, zone result, what you were unsure about, and For Claude (red-zone requests, questions, suspected rule conflicts). Without the workspace, put the same text into the PR description. Claude reviews pending logs with its /external-review skill, refactors where needed and sets review: done.
6. What only Claude does
Red-zone edits; signal, UDS, BLE and limit changes (/signal-change) and every moto-vehicle-defs edit outside docs/; new or changed decisions in DECISIONS.md; safety reviews (safety-reviewer) of invariant-8 modules; vehicle-bus tester changes; releases, tags and pins; synced files (.claude/, AGENTS.md, GEMINI.md, PLATFORM-RULES.md). You may update STATUS.md (keep ≤ 60 lines and its sections) and add rows to ISSUES.md.
7. Platform rules (copied from PLATFORM-RULES.md; "Claude" there means any AI tool unless a rule names Claude's agents or skills)
SDV-architecture diagnostics / telemetry / ADAS platform for motorcycles (first vehicle: Honda CL250). Scope (D-078): for now only a test prototype is built and tested on the CL250; the scale is widened into a commercial multi-vehicle product later, so keep vehicle facts in per-vehicle defs files and never delete a catalog DID because the CL250 does not serve it. Senior thesis → GitHub org moto-platform (11 platform repos + moto-workspace, public — D-033; never commit keys, real GPS/ride data or personal data). The user writes in Turkish. Reply in Turkish, but write every project artifact (code, comments, docs, commits) in English.
Where things are
- Platform docs live in the
moto-vehicle-defsrepo underdocs/. Depending on where you run, the path ismoto-vehicle-defs/docs/(workspace root),docs/(inside moto-vehicle-defs) orexternal/moto-vehicle-defs/docs/(consumer repo with the submodule). If none of these exist (e.g. a single-repo cloud session), say so and ask. Never guess architecture facts. - Read first:
ARCHITECTURE.md(summary) andDECISIONS.md(D-xxx decisions, Q-xxx open questions).docs/README.mdindexes the large docs by line range. Read only the needed range, never a whole raw doc. - Do not re-litigate recorded decisions. New decisions → ask the user, then record them in
DECISIONS.md.
Repo map (dependencies are one-way: everyone → moto-vehicle-defs)
| Repo | Runtime | Tooling | Role |
|---|---|---|---|
moto-vehicle-defs |
— | DBC, VSS, UDS YAML, Python codegen | Single source of truth for signals + platform docs |
moto-rt-core |
STM32H755 Cortex-M7 (D-072) | C, CMake+CubeMX, FreeRTOS | Domain controller: CAN gateway, logging, fusion, UDS/ISO-TP, bootloader, XCP, EKF, context, dyno, anomaly safety net, power management |
moto-safety-node |
STM32H755 Cortex-M4 (D-072) | C, CMake+CubeMX, bare-metal | The isolated safety partition: cornering decision + own IMU, blind-spot decision, immobilizer, LED ring and mirror LEDs |
moto-io-node |
— | — | Retired by D-072 (no code); its functions moved to safety-node and rt-core |
moto-connectivity-node |
ESP32-S3 | C, ESP-IDF | Wi-Fi/BLE + voice (ESP-SR); telemetry here is TEMPORARY |
moto-linux-node |
Raspberry Pi 5 | Python/C++ | Kuksa, CAN→VSS, lane keeping (classic CV), anomaly model, HMI, OTA |
moto-mcp |
Raspberry Pi 5 + remote server (D-077) | Python | Standalone open-source MCP server (read-only tools) |
moto-hil-bench |
STM32F4 + i7 host | C + Python | Restbus HIL bench, scenario engine, CI runner |
moto-server |
Raspi (edge) + remote, VPS-ready (D-077) | Python (FastAPI) | Ingestion, storage, MDF4/Parquet, OTA packages |
moto-ml |
Offline | Python | Training only |
moto-mobile |
Phone | Flutter (D-022) | Loosely coupled companion |
moto-workspace |
— | — | Manifest, setup script, shared Claude agents/skills (source of truth) |
HondaCl250_Telemetry |
— | archived | Read-only legacy reference (verified CL250 CAN/UDS code). Never modify it; extract facts per D-023 |
Platform invariants (non-negotiable; if one must be broken, STOP and ask)
- The ECU is read-only. The CL250 ECU only answers requests (D-019), so the vehicle bus has exactly one tester, and it only reads (D-037): rt-core (D-021); until rt-core exists, connectivity-node is the temporary sole tester (D-023), and there are never two. It sends only what
moto-vehicle-defs/uds/vehicle_cl250.yaml→tester_policyallows (D-020: session control 0x01/0x03 only, tester present, read services), through the generated gates. That policy may only be narrowed; codegen's golden copy blocks widening (D-027). Widening either one needs the user's approval, a versioned defs change andsafety-reviewer. Nothing that writes, clears, resets, unlocks, controls or reprograms the ECU, and never a map change. All other nodes stay off the vehicle bus or strictly listen-only. rt-core republishes decoded vehicle signals on the separate platform CAN bus, where our nodes talk (D-009, D-021). - Never invent signals. CAN IDs, signal names, scales, VSS paths and DIDs come only from
moto-vehicle-defs(generatedgen/code or DBC/VSS). Hand-written literals →vss-schema-guardian. - The safety path is isolated. The cornering and blind-spot decisions run on the Cortex-M4 safety partition (safety-node, D-072) and work without the Raspi, the phone, Wi-Fi, VSS, ML and the rt-core software; the partition talks to rt-core only through its E2E-checked mailbox. ML may only tighten thresholds, never loosen the safety ceiling.
- Critical work never depends on a slow channel. µs-critical → MCU + CAN. ms → Raspi. Latency-tolerant → phone/server.
- No dynamic memory on MCUs (strictly none on safety paths, avoided elsewhere). No string/DBC/VSS parsing on MCUs.
- The immobilizer acts on the starter-relay coil circuit only. Default state unlocked, hidden bypass, 10 s timeout to unlock.
- The LLM never does its own math. Position (lat/lon/height) may go only to the user's own servers and to the LLM through moto-mcp, and is never committed (provisional relaxation, D-076; revisited by Q-035).
- After any change to a safety-critical module (cornering/, safety-node, immobilizer, blind spot, watchdog/heartbeat, anomaly-safety-net, E2E, bootloader, and on the STM32H755 the inter-core mailbox, MPU/clock/reset/option-byte init and both linker scripts, D-072), run
safety-reviewerbefore committing.
Conventions
- Conventional Commits (
feat(uds): ...). Commit or push only when the user asks. - AI tools other than Claude Code follow
AGENTS.md(D-071): edit zones (red = Claude only),ext/<tool>-<topic>branches, anAI-Agent: <tool>/<model>commit trailer and a session log inmoto-workspace/external-ai/log/; Claude reviews their work with/external-review. moto-vehicle-defsuses semver. Consumers pin theexternal/moto-vehicle-defssubmodule (relative URL) to a tag. The known-good combination is recorded inmoto-workspace/manifest.yaml.- Releases (D-036): bump the build-file version (CMake
project(VERSION),pyproject.toml,pubspec.yaml) in a PR → merge after green CI →gh release create vX.Y.Z -R moto-platform/<repo> --target main --title vX.Y.Z --generate-notes→ pin the tag inmanifest.yaml(workspace PR). moto-vehicle-defs also adds aCHANGELOG.mdentry; docs-only changes need no tag. docs/archive/(Turkish originals, advisor documents) is not read by Claude (D-038).- Skills:
/repo-bootstrap(repo skeleton),/signal-change(signals),/feature-module(firmware feature),/handoff(end of session, workspace only). - Token budget is limited: delegate bulk mechanical work to
sonnet/haikusubagents, useExplorefor broad searches, one task per session.
