Compare commits

..

No commits in common. "882768af019c265b2c357a55b213a52befdf3d21" and "4e48dc2c53459f77816f7b86f1c886ec6648695b" have entirely different histories.

562
CLAUDE.md
View File

@ -1,385 +1,249 @@
# CLAUDE.md — Rules for Claude on the Argonaut 3 Project # Argonaut 3 — ROV Autonomous Inspection System
**Version:** 3.5 This file defines how Claude should behave when working in this repository.
**Updated:** 8 July 2026 Prioritise correctness, safety, and field-operational reliability above all else.
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).
--- ---
## PRIME DIRECTIVES — These override everything else # Project Identity
### 1. Research before acting. Always. - **Project:** Argonaut 3 — Autonomous Underwater ROV Inspection System
Before any technical decision, code change, or command: - **Company:** SymbyTech
- Read project docs (handover, UI design, DIR, this file) and search past chat history - **Vehicle:** Argonaut 3 based on the BlueROV2 Heavy (Blue Robotics)
- Read source code and official docs before acting; GitHub source/issues are more - **Target:** Hull and jacket surveys, no human operator during field operations
authoritative than official docs for Cockpit/BlueOS specifics - **Status:** Active development — Phase 2 (dev infrastructure) complete
- 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
--- ---
## Working Style # Tech Stack
### One command at a time - **Primary language:** Python 3 (ROS2 nodes), C++ reserved for latency-critical paths only
Give one command, wait for feedback, then give the next. Never batch commands unless asked. - **Framework:** ROS2 Jazzy Jalisco
- **Vehicle OS:** BlueOS (Blue Robotics) on Raspberry Pi 4
### Lead with context and corrections BEFORE instructions, never after. - **Autonomy compute:** Raspberry Pi 5, Ubuntu 24.04 LTS
- **MAVLink bridge:** MAVROS
### Copy-paste friendly commands - **Container runtime:** Docker (ARM64 images via buildx + QEMU)
Commands inside terminal blocks must be immediately copy-pasteable and suitable for the - **Registry:** Harbor (self-hosted) — `registry.symbytech.com`
terminal in use (Git Bash on the laptop, bash on the Pis/server). Never include: - **Source control:** Gitea (self-hosted) — `git.symbytech.com`
- Placeholders like `<YOUR_IP>` or `path/to/file` - **GCS:** Cockpit (BlueOS extension)
- Hashed `#` comments inside the command block - **Dev simulation:** BlueOS internal ardupilot-manager SITL (ArduSub 4.5.7)
- Options that require editing before running - **Package manager:** pip (Python), colcon (ROS2 workspace)
### 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.
--- ---
## CREATE-THEN-BACKUP (non-negotiable) # Repository Structure
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 ```
deferred. Backups and foundational architecture (storage, recovery, monitoring) are rov-autonomy/
prerequisites, prioritised BEFORE new feature/build work. The RPi4 SD failed with no ├── src/
backup because this was deferred — do not repeat. │ ├── rov_interfaces/ # Custom ROS2 messages and services — shared contracts
│ ├── 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.
--- ---
## Project-Specific Rules # Core Principles
### Cockpit DIY widgets - **Asset preservation over mission completion.** A recovered vehicle can repeat a mission. A lost vehicle cannot.
- Cockpit is **v1.18.2** (native desktop app mandatory — browser/Lite extension does - **Field-operational from day one.** The same codebase runs in dev and field — environment is selected by config, not by code changes.
NOT support DIY widgets) - **Safety is continuous, not reactive.** Failsafe monitoring runs at all times, not only on failure.
- JSON format with `html`/`css`/`js` string fields - **Prefer simple, readable solutions.** This code may be read under pressure in the field.
- CSS hard constraints: no `body {}`/`:root {}` global blocks, no `@keyframes`, no - **No premature optimisation.** Correctness first, performance when measured.
unicode in JS strings, no `position: fixed`, no global tag selectors - **Keep functions small and single-purpose.**
- No `async/await` in widget JS — use `.then()/.catch()` - **Do not introduce new dependencies without explicit approval.** Each dependency is a liability on embedded hardware.
- 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.
--- ---
## What Claude MUST do # Code Style
- 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)
## What Claude MUST NOT do - Follow PEP 8 for Python
- Guess at how a system works and propose commands based on that guess - Use consistent formatting already present in the file being edited
- Repeat a failed approach - Prefer explicit code over clever shorthand
- Issue commands whose outcome is uncertain - Always add full inline comments — every function, every non-obvious line
- Provide partial code requiring manual editing to work - Use meaningful variable and function names — no abbreviations unless standard ROS2/MAVLink convention
- Assume a system is stable without checking - Avoid deeply nested logic (>3 levels)
- Use `print()` in ROS2 nodes; hardcode addresses/ports/thresholds - No commented-out code in final output
- Build x86 Docker images for vehicle deployment - All ROS2 nodes must have a module-level docstring listing: purpose, subscribed topics, published topics, services
- 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
--- ---
## Documentation discipline # ROS2 Rules
- All project docs (handover, UI design, DIR, this file, sim/ML plan) are maintained as
COMPLETE replacement files uploaded to Claude project knowledge — never patches or - All custom messages live in `rov_interfaces` — do not define messages inside other packages
fragments, never committed to Gitea. - Always declare parameters explicitly with `declare_parameter()` before `get_parameter()`
- Version numbers and changelogs maintained on every update. - Load configuration from YAML files via launch — do not hardcode values in nodes
- Only code and README files go to Gitea. - Use `self.get_logger()` for all logging — never `print()`
- 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
--- ---
## SESSION LOG (bridges Claude Code → claude.ai chat) # Failsafe Rules
**Why this exists:** Claude Code (VS Code) and the claude.ai chat are separate systems with **These are safety-critical. Do not change without explicit instruction and design review.**
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.
**Format:** `YYYY-MM-DD — one-line summary of decision/change (commit hash if applicable)` - Assessment states: GREEN / AMBER / RED — defined in `FailsafeStatus.msg`
- Assessment runs continuously at dynamic rates: 5 Hz (GREEN), 20 Hz (AMBER), 50 Hz (RED)
**Claude Code MUST append an entry here at the end of any session where one of these - Priority order is fixed — see `ROV_Failsafe_Design_v2.0.md`
happened** — not for routine edits, refactors, or in-progress work: - All thresholds are configurable via YAML — never hardcode safety values
- A design/architecture decision was made or changed (DIR-level, mode-flag behaviour, - `EMERGENCY_SURFACE` is the last resort — it must never be the first response
failsafe priority, recording behaviour) - Comms timeout default: 2 seconds — configurable per deployment
- A hardware fact was confirmed or changed (wiring, storage layout, board config) - Battery thresholds: warning 25%, return 20%, critical 12%, emergency 8%
- 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`)
--- ---
## Key Reference Documents (current versions) # Package-Specific Rules
**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_21.md` | Master reference — architecture, environment, commands, status board | | `ROV_Project_Handover_v2.3.md` | Master reference — architecture, environment, commands |
| `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 | | `ROV_Failsafe_Design_v2.0.md` | Failsafe state machine, sensor roadmap, priority order |
| `ROV_Failsafe_Design_v2_0.md` | Failsafe state machine, sensor roadmap, priority order | | `Argonaut3_UI_Design_v1.1.md` | Cockpit extension and widget specifications |
| `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
--- ---
## Safety Notice # Notes
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, 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.
and reliability are non-negotiable. Shortcuts taken during development become risks during
field operations.