Instructions for AI coding agents (Codex, Claude Code, etc.) working on QGroundControl.
- CODING_STYLE.md — Naming, formatting, C++20 features, QML style, logging
- .github/CONTRIBUTING.md — Architecture patterns (Fact System, Multi-Vehicle, FirmwarePlugin)
- tools/README.md — Development scripts and tooling
- test/README.md — Test framework, base classes, CTest labels, MultiSignalSpy, coverage
- .github/ci-overview.md — CI workflow/action/script layout and conventions
- .pre-commit-config.yaml — All enforced linters (clang-format, clang-tidy, ruff, pyright, shellcheck, actionlint, zizmor, qmllint, clazy, vehicle-null-check, check-no-qassert, check-no-qtest-ignore-message)
These are the non-negotiables. The first four are QGC's core architecture patterns; the rest are enforced by pre-commit hooks, so ignoring them wastes a build cycle. Full list with code examples: .github/CONTRIBUTING.md#architecture-patterns and CODING_STYLE.md#common-pitfalls.
- Fact System — ALL vehicle parameters flow through Facts; never create custom parameter storage.
- Multi-Vehicle — ALWAYS null-check
activeVehicle()/Vehicle*before dereferencing (vehicle-null-check). - Firmware Plugin — use
vehicle->firmwarePlugin()for firmware-specific behavior, notif (px4)branches. - QML Integration — register types with
QML_ELEMENT/QML_SINGLETON/QML_UNCREATABLE; expose state viaQ_PROPERTY. - No
Q_ASSERTin production code — use defensive checks with early returns (check-no-qassert). - No
QTest::ignoreMessagein tests — useexpectLogMessage/ignoreLogMessage(check-no-qtest-ignore-message). - No fixed-delay
QTest::qWait(<n>)— useQTRY_*_WITH_TIMEOUTorQSignalSpy::wait(check-no-fixed-qwait).
src/FactSystem/Fact.h— Parameter system foundationsrc/Vehicle/Vehicle.h— Core vehicle modelsrc/FirmwarePlugin/FirmwarePlugin.h— Firmware abstraction
Key modules (full tree under src/ — ~33 subdirectories):
src/
├── Vehicle/ # Vehicle state/comms
├── Comms/ # Link layer (serial, UDP, TCP, Bluetooth)
├── FactSystem/ # Parameter management
├── FirmwarePlugin/ # PX4/ArduPilot abstraction
├── AutoPilotPlugins/ # Vehicle setup UI
├── MissionManager/ # Mission planning
├── MAVLink/ # Protocol handling
├── VideoManager/ # Video pipeline (GStreamer)
├── FlyView/ # In-flight UI
├── PlanView/ # Mission planning UI
├── QmlControls/ # Reusable QML components
└── Settings/ # Persistent settings
The just recipes are the canonical workflow — see tools/README.md for the full list.
.github/ci-overview.md documents how CI invokes builds and tests; match CI, don't guess.
just configure # CMake configure (pulls submodules first)
just build # incremental build; uses all cores (override with JOBS=N)
just test # ctest, LABELS="Unit|Integration" EXCLUDE="Flaky|Network"
just lint # fast pre-commit gate (clang-format, ruff, qmllint, ...)
just check # lint + test (run before declaring done)
just format-fix # apply clang-format / ruff-format
just info # print resolved versions (Qt, CMake, GStreamer)- Build incrementally — rebuild every few file edits during multi-file C++/Qt work, not just at the end; fix build errors before continuing.
- Tight test loops — iterate one test with
ctest -R <name>(or--gtest_filter); only run the full label on the final pass. CI runsctest --output-on-failure -L Unit. - Match CI — before running tests/lint locally, use the same command CI runs (.github/ci-overview.md), not a local guess.
Before considering a change complete:
just buildsucceeds.just lint(orpre-commit run --all-filesfor the full sweep) passes.- Relevant tests pass (
ctest -R <name>for the touched area; full-L Uniton the final pass). - Commit message follows Conventional Commits (below).
Commit messages follow Conventional Commits — the type drives release automation
(.releaserc.json → semantic-release). Use: feat, fix, perf, revert (release-triggering);
docs, style, chore, refactor, test, build, ci (no release). Example: fix(Vehicle): guard null activeVehicle in telemetry handler.
Your output will be reviewed by another AI agent before being accepted. Keep changes focused and minimal, use clear naming, and leave explanatory commit messages. Avoid unrelated changes, commented-out code, or ambiguous TODOs.
Key Principle: Match the style of code you're editing. See CODING_STYLE.md for conventions and CODING_STYLE.md#examples for canonical Vehicle/Fact/QML snippets.