Compare commits

...

2 Commits

562
CLAUDE.md
View File

@ -1,249 +1,385 @@
# Argonaut 3 — ROV Autonomous Inspection System # CLAUDE.md — Rules for Claude on the Argonaut 3 Project
This file defines how Claude should behave when working in this repository. **Version:** 3.5
Prioritise correctness, safety, and field-operational reliability above all else. **Updated:** 8 July 2026
This is a real autonomous vehicle system. Code runs on hardware in water.
**Changelog 3.5:** Added `[VSC]` terminal label. Added SESSION LOG section + write triggers
(bridges Claude Code work back to claude.ai chat, which has no native access to Claude Code
sessions).
--- ---
# Project Identity ## PRIME DIRECTIVES — These override everything else
- **Project:** Argonaut 3 — Autonomous Underwater ROV Inspection System ### 1. Research before acting. Always.
- **Company:** SymbyTech Before any technical decision, code change, or command:
- **Vehicle:** Argonaut 3 based on the BlueROV2 Heavy (Blue Robotics) - Read project docs (handover, UI design, DIR, this file) and search past chat history
- **Target:** Hull and jacket surveys, no human operator during field operations - Read source code and official docs before acting; GitHub source/issues are more
- **Status:** Active development — Phase 2 (dev infrastructure) complete authoritative than official docs for Cockpit/BlueOS specifics
- Confirm facts from sources, not from memory
If uncertain: **search first, then act**. Never guess.
### 2. Time and tokens are finite and valuable
- **Time wasting is FORBIDDEN. Token wasting is FORBIDDEN.**
- Short replies. No preamble padding. To the point.
- Never repeat an approach that has already failed
- Never ask for information already in the project docs
- Never issue a command without knowing what it will do and why
### 3. Facts only. Never assumptions.
- Do not proceed based on what "should" work
- Do not assume port, routing, or API behaviour — measure it
- Do not infer system state — verify it before issuing commands
- If a claim cannot be verified from a source, say so explicitly
- Never present a prior session's confirmed state as the current running state
--- ---
# Tech Stack ## Working Style
- **Primary language:** Python 3 (ROS2 nodes), C++ reserved for latency-critical paths only ### One command at a time
- **Framework:** ROS2 Jazzy Jalisco Give one command, wait for feedback, then give the next. Never batch commands unless asked.
- **Vehicle OS:** BlueOS (Blue Robotics) on Raspberry Pi 4
- **Autonomy compute:** Raspberry Pi 5, Ubuntu 24.04 LTS ### Lead with context and corrections BEFORE instructions, never after.
- **MAVLink bridge:** MAVROS
- **Container runtime:** Docker (ARM64 images via buildx + QEMU) ### Copy-paste friendly commands
- **Registry:** Harbor (self-hosted) — `registry.symbytech.com` Commands inside terminal blocks must be immediately copy-pasteable and suitable for the
- **Source control:** Gitea (self-hosted) — `git.symbytech.com` terminal in use (Git Bash on the laptop, bash on the Pis/server). Never include:
- **GCS:** Cockpit (BlueOS extension) - Placeholders like `<YOUR_IP>` or `path/to/file`
- **Dev simulation:** BlueOS internal ardupilot-manager SITL (ArduSub 4.5.7) - Hashed `#` comments inside the command block
- **Package manager:** pip (Python), colcon (ROS2 workspace) - Options that require editing before running
### Code comments
When writing code (Python, config, scripts), always include full inline comments
explaining what each section does and why. This is a standing user preference.
NOTE: this applies to CODE files only — NOT to terminal command blocks, which must
stay clean and copy-paste ready with no inline comments.
### Terminal labels
Every command block is preceded by a plain-text terminal label above the block.
Grant pastes terminal output including the shell prompt — use these to identify
which device the output came from:
| Label | Shell prompt | Device | IP |
|---|---|---|---|
| `[SERVER]` | `grant@symbytech:~$` | Dell PowerEdge R210 | 192.168.1.175 |
| `[RPi5]` | `ubuntu@rov-brain:~$` | Raspberry Pi 5 autonomy brain | 192.168.1.101 |
| `[RPi4]` | `pi@blueos:~$` | Raspberry Pi 4 BlueOS gateway | 192.168.1.100 |
| `[LAPTOP — Git Bash]` | `grant@BB8 MINGW64 ...` | Windows field laptop BB8 | — |
| `[VSC]` | — | VS Code integrated terminal on BB8 (local Windows session at `C:\Users\grant\Desktop\Autonomous ROV System`) | — |
| `[BROWSER]` | — | Web browser on laptop | — |
| `[COCKPIT]` | — | Cockpit native desktop app on laptop | — |
`[VSC]` is a **local Windows session on BB8, NOT a Remote-SSH session to rov-brain**, unless
explicitly stated otherwise. Do not assume `[VSC]` == RPi5 working copy.
Never put the label inside the bash block.
> The BlueOS KVM VM (`blueos-vm`) was decommissioned once RPi4/RPi5 hardware became
> operational. `[VM]` is no longer a valid label. Any VM artefacts remaining on the
> server are for reference only.
### Verify before modifying
Always read a file before modifying it. Always check current state before changing it.
State what you observed before proposing a change.
### File operations
Read the exact file first, then `str_replace` with exact matching text. Remove old
versions before writing new ones. Complete files, not fragments — no partial code.
### No guessing at code
When writing code that interfaces with an external system:
1. Find the official documentation or source code
2. Find a working example from the official repo or community
3. Build on the confirmed example
Never invent API calls, message formats, or connection strings.
### Own mistakes once
Acknowledge an error once and fix it. No repeated apologies, no grovelling.
### KISS
Recommend the simple correct path, not a menu of options.
--- ---
# Repository Structure ## CREATE-THEN-BACKUP (non-negotiable)
When any new component (device, drive, service, config, OS install) is created or
``` configured, a backup must be put in place immediately as part of that work — not
rov-autonomy/ deferred. Backups and foundational architecture (storage, recovery, monitoring) are
├── src/ prerequisites, prioritised BEFORE new feature/build work. The RPi4 SD failed with no
│ ├── rov_interfaces/ # Custom ROS2 messages and services — shared contracts backup because this was deferred — do not repeat.
│ ├── rov_navigation/ # State estimation, EKF, depth fusion
│ ├── rov_perception/ # Camera nodes, feature detection
│ ├── rov_control/ # Failsafe monitor, motion controller
│ ├── rov_mission/ # Mission executor, waypoint sequencing
│ ├── rov_bringup/ # Top-level launch files
│ └── rov_simulation/ # DEV ONLY — mock publishers, test scenarios
├── Dockerfile # ARM64 image build — targets RPi5
├── docker-entrypoint.sh # Container startup — sources ROS2 + workspace
└── CLAUDE.md # This file
```
**Critical rule:** `rov_simulation` is dev-only. Never include it in production builds or deploy it to vehicle hardware.
--- ---
# Core Principles ## Project-Specific Rules
- **Asset preservation over mission completion.** A recovered vehicle can repeat a mission. A lost vehicle cannot. ### Cockpit DIY widgets
- **Field-operational from day one.** The same codebase runs in dev and field — environment is selected by config, not by code changes. - Cockpit is **v1.18.2** (native desktop app mandatory — browser/Lite extension does
- **Safety is continuous, not reactive.** Failsafe monitoring runs at all times, not only on failure. NOT support DIY widgets)
- **Prefer simple, readable solutions.** This code may be read under pressure in the field. - JSON format with `html`/`css`/`js` string fields
- **No premature optimisation.** Correctness first, performance when measured. - CSS hard constraints: no `body {}`/`:root {}` global blocks, no `@keyframes`, no
- **Keep functions small and single-purpose.** unicode in JS strings, no `position: fixed`, no global tag selectors
- **Do not introduce new dependencies without explicit approval.** Each dependency is a liability on embedded hardware. - No `async/await` in widget JS — use `.then()/.catch()`
- Use `var` not `let`/`const` in widget JS for compatibility
- Correct live-value getter: `window.cockpit.getDataLakeVariableData(variableId)`
(CONFIRMED v1.18.2). `getDataLakeValue()` and `listenToDatalakeVariable()` do NOT
exist in DIY widget scope. `getAllDataLakeVariablesInfo()` is metadata-only.
- Widget JSON files in the repo use underscore naming: `w6_altitude.json` (matches
W0W5). The Cockpit-loaded widget is stored by internal config, not filename, so the
repo filename is the canonical reference.
### Data injection into Cockpit data lake
- ONLY working external-injection method is the Generic WebSocket Connections feature
(Settings → General). Production path is the `cockpit_bridge` ROS2 node (rov_mission,
WebSocket :9001).
- Message format: `variable-name=value` (one per send). Data lake key: `external/{name}`.
- ALWAYS use hyphens — Cockpit normalises underscores to hyphens in IDs.
- HTTP POST to mavlink2rest does NOT work for data lake injection. MAVLink UDP injection
from external scripts does NOT work. Tested and confirmed — do not suggest them.
- Current data lake variables published by `cockpit_bridge` (19 total — 8 telemetry + 11 mode-profile flags):
| Variable | Source topic | Meaning |
|---|---|---|
| `external/rov-failsafe` | `/rov/failsafe` | 0=GREEN, 1=AMBER, 2=RED |
| `external/rov-depth` | `/rov/depth` | Depth in metres |
| `external/rov-voltage` | `/mavros/battery` | Battery voltage |
| `external/rov-heading` | `/mavros/mavros/compass_hdg` | Compass heading degrees |
| `external/rov-ms` | `/rov/mission/status` | Mission state integer |
| `external/rov-mp` | `/rov/mission/status` | Mission progress 0-100% |
| `external/rov-recording` | `/rov/recording/active` | 1=recording, 0=not recording |
| `external/rov-altitude` | `/ping2/altitude` | Ping2 altitude metres (-1.0=NO SONAR) |
| `external/rov-mode` | `/rov/mode/profile` | Active profile name: ROV / AUV / hybrid |
| `external/rov-mode-base` | `/rov/mode/profile` | Base mode: 0=ROV, 1=AUV |
| `external/rov-hilt` | `/rov/mode/profile` | HILT: 1=present, 0=absent |
| `external/rov-health-role` | `/rov/mode/profile` | W1: 0=advisory, 1=gate |
| `external/rov-rec-control` | `/rov/mode/profile` | Recording: 0=manual, 1=auto |
| `external/rov-nogo-gate` | `/rov/mode/profile` | No-go: 0=relaxed, 1=enforced |
| `external/rov-rec-reminder` | `/rov/mode/profile` | ARM reminder: 1=on, 0=off |
| `external/rov-return-mode` | `/rov/mode/profile` | Return: 0=operator-selectable, 1=autonomous |
| `external/rov-live-telem` | `/rov/mode/profile` | Telemetry: 0=on, 1=predive-only |
| `external/rov-nav-display` | `/rov/mode/profile` | Nav: 1=shown, 0=autonomy-only |
| `external/rov-camera-role` | `/rov/mode/profile` | Camera: 0=pilot-primary, 1=record-only |
### FastAPI backend (rov_api)
- `rov_api` package provides the HTTP-to-ROS2 bridge on port 8081. Runs as
`argonaut-api.service` (manual-start in dev, same policy as argonaut.service).
- Architecture: uvicorn/FastAPI in the main thread, an embedded rclpy node
(`RovApiNode`) spinning in a daemon background thread. HTTP handlers call thread-safe
methods on the node. Do NOT call `rclpy.spin()` more than once.
- Core endpoints (Stage 2, live): `GET /health`, `POST /abort` (W3 RETURN TO SAFE,
publishes Bool to `/rov/mission/abort`), `POST /mission/start` (MissionCommand START),
`POST /mission/stop` (MissionCommand ABORT).
- Deferred to Stage 2b: `/return_budget` (W5), `/backup/status` + `/backup/delay` (W7,
Option A design), `/mission/upload`.
- **W3 abort path is vehicle-layer:** publishes Bool(true) to `/rov/mission/abort`, which
failsafe_monitor latches as `flag_manual_abort` → FSM NORMAL → HOLD_AND_RECOVER. This is
DIR-5 return-to-safe, NOT the mission ABORT service. Do not confuse the two.
- **Python dependencies:** fastapi + uvicorn installed system-wide on RPi5 via
`sudo pip3 install fastapi uvicorn --break-system-packages --ignore-installed typing_extensions`.
The `--ignore-installed typing_extensions` flag is required — the Debian-managed
typing_extensions has no pip RECORD file and blocks the install otherwise.
### Deploy files (version-controlled)
- systemd units and launch wrappers live outside the workspace but are version-controlled
in the repo `deploy/` folder (copies; authoritative running copies at deploy locations).
- `deploy/argonaut.service``/etc/systemd/system/argonaut.service`
- `deploy/argonaut-stack.sh``/usr/local/bin/argonaut-stack.sh`
- `deploy/argonaut-api.service``/etc/systemd/system/argonaut-api.service`
- `deploy/argonaut-api.sh``/usr/local/bin/argonaut-api.sh`
- `deploy/README.md` documents locations, pip deps, and install procedure.
- `/home/ubuntu/ros2_ws` is a symlink to `/data/ros2_ws` — both wrappers rely on it.
### Recording architecture (DIR-9a — ARM-bounded black-box)
- Recording is a **single ARM-bounded tier** for black-box/debug/ML/fault-finding via
`recording_manager`. **AUV:** starts on ARM, stops on DISARM. **ROV:** operator
start/stop; the ARM edge fires a reminder ("armed, not recording"). Keyed to the
`recording_control` flag, not the mode. (Supersedes the old continuous DIR-9 model.)
- **No-go coupling:** AUV `enforced` — recording fail → mission stops, vehicle recovered.
ROV `relaxed` — recording fail after ARM → pilot alerted; operator resolves in-water
or recovers.
- Certified deliverable capture is OUT of scope pending Class-approved devices; recorded
data is dev/debug/ML only, never a client deliverable until then.
- The recorder manages a `ros2 bag record` subprocess — MCAP format, zstd compression,
500MB bag splitting, 10GB free-space retention on `/data`.
- **`foxglove_bridge` is visualisation only — it does NOT record MCAP.** Correct this
wherever the old "MCAP via foxglove_bridge" claim appears.
- Bags written to `/data/bags/dive_<timestamp>/` on NVMe.
- No camera in the topic set currently — recording ~2.5 MB/s (nav/MAVLink only).
- **Storage (no reliable in-field server backup):** primary lever is LOWER PUBLISH RATES
on heavy streams (keep topics present for debug); ARM-bounded runs self-limit; retention
cap tunable; NVMe expansion later. Topic/rate review is an open action.
### Operating-mode profiles (DIR-1/2, mode_profiles.yaml)
- One widget set; behaviour is set by flags from a mode profile. Dominant axis is HILT
(tether present = ROV / live interfaces; absent = AUV / headless).
- **Source of truth:** `mode_profiles.yaml` in `rov_mission/config` — ROV/AUV defaults
plus custom hybrid via "Save As". Hybrid is NOT a third mode; it is any saved profile
with overrides.
- `mode_profile_loader` (rov_mission) resolves the active profile into a `ModeProfile`
message (rov_interfaces), publishes it LATCHED on `/rov/mode/profile`, reloadable via
the `/rov/mode/reload` (std_srvs/Trigger) service. Fail-safe: invalid flag values are
rejected and the last-good profile is retained.
- `cockpit_bridge` surfaces the 11 flags to the data lake as `external/rov-*`. Widgets
read the flags, NOT the mode.
- Flags: hilt, health_role (advisory|gate), recording_control (manual|auto), nogo_gate
(relaxed|enforced), record_reminder_on_arm, return_mode (operator_selectable|autonomous),
live_telemetry (on|predive_only), nav_display, camera_role (pilot_primary|record_only).
- **YAML gotcha:** YAML 1.1 parses bare `on`/`off`/`yes`/`no` as booleans — quote enum
values like `"on"`. The loader also normalises bool→string as a guard.
### BlueOS / MAVLink router
- **MAVLinkRouter** is the confirmed router. Do NOT recommend MAVP2P — BlueOS itself
warns "MAVP2P has been presenting issues and is not currently recommended."
- MAVP2P was tried and reverted. Do not suggest it again.
- The BlueOS ardupilot-manager FD exhaustion bug (Docker default `nofile=1024`) is
fixed permanently via `/etc/docker/daemon.json` with `nofile` hard/soft = 65536.
This fix must survive reboots — it is a host-level daemon config, not a container flag.
Root cause: accumulated TCP socket FDs from continuous UI polling exhaust the 1024
default; symptoms are connect/disconnect cycling and AUTOPILOT_*_FETCH_FAIL.
### Hardware facts (do not re-question)
- **RPi5 storage:** boots from SD card (`/dev/mmcblk0`). NVMe holds DATA ONLY (`/data` —
ros2_ws, bags, logs). Backup images the SD for OS, rsyncs `/data` for data.
- **Flight controller:** Pixhawk 2.4.8 on `/dev/ttyACM0` is the active flight controller.
SITL is NOT used in production. BlueOS `preferred_board` = Pixhawk1.
- **Tailscale on RPi5:** installed but stopped and disabled (`tailscaled.service` disabled
2 Jul 2026). Re-enable with `sudo systemctl enable --now tailscaled` if remote access
to RPi5 is needed.
### Hostname resolution (DNS)
- Hostnames `symbytech` (192.168.1.175), `blueos` (192.168.1.100), and `rov-brain`
(192.168.1.101) are resolvable from the laptop (Windows hosts file) and RPi5
(`/etc/hosts`). Grant prefers hostnames over IPs.
- Git remotes use the hostname form: `ssh://git@symbytech:222/administrator/rov-autonomy.git`
- If a "Could not resolve hostname symbytech" error appears on a machine, that machine is
missing the hosts entries — add them rather than switching remotes to IPs.
### Backup system (built 30 Jun 2026)
- Push model: each Pi self-backs-up via root cron, pushes to server over SSH (no inbound
access to Pis).
- RPi4: `/usr/local/bin/rov-brain-pi4-backup.sh`, 00:00 daily.
- RPi5: `/usr/local/bin/rov-brain-pi5-backup.sh`, 00:30 daily (staggered).
- Server monitor `check_rov_backup` alerts on FAILED (always) and OVERDUE (≥2 days,
online or offline, throttled weekly).
- Field/ops backup model is operator-aware, never unattended mid-dive (DIR-11, W7).
### argonaut.service / argonaut-api.service (RPi5)
- Manual start is deliberate for both. Verify BlueOS heartbeat first, then
`sudo systemctl start argonaut.service`. Do NOT enable auto-start until field-ready.
Restate this rule each time it comes up.
- `argonaut-api.service` orders After=argonaut.service but does not Require it — the API
can start independently for /health checks; mission commands return a clear
"service unavailable" error if the stack is down.
### ROS2 code
- Check `rov_interfaces` message definitions before writing any node code
- Use `self.get_logger()` not `print()`
- Do not hardcode IPs, ports, or thresholds
- Do not deploy `rov_simulation` in production launch files
- Editing an installed launch/config requires `colcon build --packages-select <pkg>`
the running launch uses the installed copy under install/, not src/
- ARM64 Docker builds on the laptop only (buildx), never on the server
### Git workflow
- Commit to `master` on `administrator/rov-autonomy`
- Gitea SSH remote (hostname form): `ssh://git@symbytech:222/administrator/rov-autonomy.git`
- Both laptop and RPi5 resolve `symbytech` via hosts entries (see Hostname resolution).
- Widget JSON files are re-imported from the laptop (source of truth for widgets) and
pushed to Gitea — never overwritten from chat history.
- UFW rules before starting new services on new ports.
### Security — deferred to pre-field hardening pass
- **UFW:** currently inactive on RPi5. Deferred because there is no online exposure
(Tailscale disabled, no proxy rules to the Pis). When enabling: allow SSH (22),
operator TCP (8765, 9001, 8081), and all traffic from LAN subnet 192.168.1.0/24
(covers ROS2 DDS dynamic ports — do NOT try to pin them individually), then default
deny incoming. Bundle with SSH password change. Trigger: before remote ops or field.
- **RPi5 SSH password:** weak dev password, password auth enabled. Change to strong
password before field deployment.
--- ---
# Code Style ## What Claude MUST do
- State confirmed facts before proposing an action; cite the source of any technical claim
- Acknowledge when something has already been tried and failed
- Provide complete, working code (fully commented) based on confirmed examples
- Verify system state before and after changes
- Update the handover doc at the end of each session (version-controlled, changelog)
- Follow PEP 8 for Python ## What Claude MUST NOT do
- Use consistent formatting already present in the file being edited - Guess at how a system works and propose commands based on that guess
- Prefer explicit code over clever shorthand - Repeat a failed approach
- Always add full inline comments — every function, every non-obvious line - Issue commands whose outcome is uncertain
- Use meaningful variable and function names — no abbreviations unless standard ROS2/MAVLink convention - Provide partial code requiring manual editing to work
- Avoid deeply nested logic (>3 levels) - Assume a system is stable without checking
- No commented-out code in final output - Use `print()` in ROS2 nodes; hardcode addresses/ports/thresholds
- All ROS2 nodes must have a module-level docstring listing: purpose, subscribed topics, published topics, services - Build x86 Docker images for vehicle deployment
- Change the failsafe priority order without design review
- Present prior-session state as current running state
- Recommend MAVP2P as the MAVLink router
- Commit docs to Gitea — only code and README go to Gitea; docs live in Claude project knowledge
--- ---
# ROS2 Rules ## Documentation discipline
- All project docs (handover, UI design, DIR, this file, sim/ML plan) are maintained as
- All custom messages live in `rov_interfaces` — do not define messages inside other packages COMPLETE replacement files uploaded to Claude project knowledge — never patches or
- Always declare parameters explicitly with `declare_parameter()` before `get_parameter()` fragments, never committed to Gitea.
- Load configuration from YAML files via launch — do not hardcode values in nodes - Version numbers and changelogs maintained on every update.
- Use `self.get_logger()` for all logging — never `print()` - Only code and README files go to Gitea.
- Timers must be named and cancellable when rate changes are required
- Topic names use the `/rov/` prefix namespace for all custom topics
- Always include a `Header` with timestamp in custom messages
- Destroy nodes cleanly in `finally` blocks
--- ---
# Failsafe Rules ## SESSION LOG (bridges Claude Code → claude.ai chat)
**These are safety-critical. Do not change without explicit instruction and design review.** **Why this exists:** Claude Code (VS Code) and the claude.ai chat are separate systems with
no shared memory. Claude Code reads this file at session start but the claude.ai chat cannot
see Claude Code's session history at all. This log is the bridge.
- Assessment states: GREEN / AMBER / RED — defined in `FailsafeStatus.msg` **Format:** `YYYY-MM-DD — one-line summary of decision/change (commit hash if applicable)`
- Assessment runs continuously at dynamic rates: 5 Hz (GREEN), 20 Hz (AMBER), 50 Hz (RED)
- Priority order is fixed — see `ROV_Failsafe_Design_v2.0.md` **Claude Code MUST append an entry here at the end of any session where one of these
- All thresholds are configurable via YAML — never hardcode safety values happened** — not for routine edits, refactors, or in-progress work:
- `EMERGENCY_SURFACE` is the last resort — it must never be the first response - A design/architecture decision was made or changed (DIR-level, mode-flag behaviour,
- Comms timeout default: 2 seconds — configurable per deployment failsafe priority, recording behaviour)
- Battery thresholds: warning 25%, return 20%, critical 12%, emergency 8% - A hardware fact was confirmed or changed (wiring, storage layout, board config)
- A security-relevant change was made (SSH, UFW, credentials, exposure)
- A backup/recovery-relevant change was made
- A bug was root-caused and fixed (not just patched)
- A previously-deferred item was completed or its trigger condition changed
- Anything Grant would reasonably expect the claude.ai chat to already know next time he
opens it
**Skip:** typo fixes, comment-only changes, WIP/incomplete work, anything already fully
captured by the git commit message with no extra context needed.
**Workflow:** Grant pastes new entries into the claude.ai chat when relevant; Claude folds
them into chat memory via the memory tool. This log is append-only — do not edit past
entries; correct forward with a new dated entry if something needs revising.
- 2026-07-05 — Widget housekeeping: added W6 altitude widget, removed probe diagnostics
widget (`785ec56`)
--- ---
# Package-Specific Rules ## Key Reference Documents (current versions)
**rov_interfaces**
- Changing a message definition requires updating all nodes that use it
- Constants in messages use ALL_CAPS naming
- Never remove a field from a message without confirming no node depends on it
**rov_control**
- `failsafe_monitor.py` must always be the first node started in any launch file
- Do not modify the priority order in `_apply_failsafe_priority()` without design review
**rov_mission**
- Waypoints are loaded from YAML files — never hardcoded
- The breadcrumb buffer must be cleared at mission start and never between waypoints
- Entry point is recorded once at mission start — do not update it during flight
**rov_simulation**
- Every file in this package must have the `DEV ONLY — NOT FOR DEPLOYMENT` warning in its docstring
- Scenario names must be documented in the node docstring
- Never subscribe to real hardware topics from simulation nodes
**rov_bringup**
- Launch files must load config from the package share directory — never from absolute paths
- The `env` argument selects between `dev` and `field` configs — always provide a default of `dev`
---
# Docker / Build Rules
- Target architecture: `linux/arm64` — always build with `--platform linux/arm64`
- Build script: `~/build-rov.sh [tag]` — defaults to `:dev`
- Never build x86 images for the vehicle — they will silently fail on RPi5
- The `rov_simulation` package must be excluded from production image builds
- Do not modify the `Dockerfile` base image without checking ROS2 Jazzy compatibility
---
# Git Rules
- Commit messages follow conventional commits format: `feat:`, `fix:`, `docs:`, `refactor:`
- Do not commit directly to `master` without instruction
- Keep commits focused — one logical change per commit
- Always commit config changes alongside the code that depends on them
- Branch: `master` is the working branch for this project
---
# Dev Environment
**Terminal conventions (always use these labels):**
- `[SERVER]` — SSH session on SymbyTech server (192.168.1.175 / Tailscale: 100.104.236.104)
- `[VM]` — SSH session on BlueOS VM (192.168.122.89 / Tailscale: 100.84.141.120)
- `[GIT]` — Git Bash on laptop
- `[LAPTOP]` — Windows Command Prompt on laptop
**Key addresses:**
- BlueOS: `http://100.84.141.120`
- Cockpit: via BlueOS sidebar
- Harbor: `https://registry.symbytech.com`
- Gitea: `https://git.symbytech.com`
**BlueOS session requirements:**
- Enable Pirate Mode (skull icon) each session — resets due to no bootstrap container
- Cockpit MAVLink2REST: `ws://100.84.141.120/mavlink2rest/ws/mavlink`
- Vehicle network connection: `100.84.141.120`
---
# Testing
- `rov_simulation` provides mock publishers for all MAVROS topics
- Five test scenarios: `nominal`, `low_battery`, `comms_loss`, `depth_warning`, `all_clear`
- Dev stack launch: `ros2 launch rov_simulation dev_stack.launch.py scenario:=<name>`
- Do not remove existing test scenarios without confirming intent
- Add new scenarios for any new failsafe condition added to the design
---
# Claude Behaviour Rules
- Always read existing code in a file before modifying it
- Always read the relevant design document before implementing a feature
- Always check message definitions in `rov_interfaces` before writing node code
- Explain assumptions before making architectural changes
- If requirements are unclear, ask before implementing
- Prefer incremental changes over large rewrites
- Never delete code without confirming intent
- If a safer or simpler approach exists, suggest it before proceeding
- Do not refactor unrelated code
- Do not optimise prematurely
---
# What Claude SHOULD do
- Implement features step-by-step with verification at each step stating if feedback is expected
- Fix bugs with minimal disruption to surrounding code
- Add comments to every function and non-obvious block
- Highlight safety risks when they exist
- Suggest better approaches when appropriate
- Verify file structure and entry points after creating new nodes
---
# What Claude should NOT do
- Do not restructure the package layout unless explicitly asked
- Do not introduce new Python packages without approval
- Do not rewrite working code for style
- Do not assume missing requirements — ask
- Do not hardcode thresholds, addresses, or paths
- Do not deploy or reference `rov_simulation` in production launch files
- Do not change the failsafe priority order without design review
- Do not use `print()` — always use `self.get_logger()`
- Do not build x86 Docker images for vehicle deployment
---
# When Uncertain
1. Stop implementation
2. State the ambiguity clearly
3. Provide a suggested approach with trade-offs
4. Wait for confirmation before proceeding
---
# Key Design Documents
All in the Claude project knowledge base:
| Document | Purpose | | Document | Purpose |
|---|---| |---|---|
| `ROV_Project_Handover_v2.3.md` | Master reference — architecture, environment, commands | | `ROV_Project_Handover_v2_21.md` | Master reference — architecture, environment, commands, status board |
| `ROV_Failsafe_Design_v2.0.md` | Failsafe state machine, sensor roadmap, priority order | | `design_intent_register_DRAFT.md` | DIR-1…DIR-11 (+ DIR-9a) — owner/operator intent, source of truth for *why*; recording now ARM-bounded per DIR-9a |
| `Argonaut3_UI_Design_v1.1.md` | Cockpit extension and widget specifications | | `ROV_Failsafe_Design_v2_0.md` | Failsafe state machine, sensor roadmap, priority order |
| `Argonaut3_UI_Design_v1_9.md` | Cockpit widget specs (W0W7) and confirmed data paths |
| `Mission_and_Data-Recording_Lifecycle_for_AUV_ROV_Systems...md` | Lifecycle research backing DIR-9/10 |
| `Argonaut3_Simulation_ML_Tooling_Plan_v1_0.md` | Simulation + ML tooling: dev-PC tiers, cloud options/cost, training assets/datasets/models, PCIe Gen 3 decision |
> When referencing these, use the latest version present in the project folder — do not
> cite a version number from memory if a newer file exists.
Always use the highest version
--- ---
# Notes ## Safety Notice
This is a production-oriented autonomous safety system. All code must be treated as if it
This is a production-oriented autonomous safety system. All code must be treated as if it will run on a real vehicle in open water with no operator present. Safety, correctness, and reliability are non-negotiable. will run on a real vehicle in open water with no operator present. Safety, correctness,
and reliability are non-negotiable. Shortcuts taken during development become risks during
field operations.