docs: document configuration and diagnostics contracts

This commit is contained in:
Onur Özkan committed 2026-08-09 22:08:20 +03:00
1 parent 13e4040ee3
commit 9646451645
11 files changed
+336 -15

No files matched your search

+3
View File
@@ -6,6 +6,9 @@
- [Architecture](architecture.md)
- [Safety boundary](safety-boundary.md)
- [MAVLink and telemetry](mavlink.md)
- [Configuration](configuration.md)
- [Logs](logs.md)
- [Errors](errors.md)
- [Boards](boards/index.md)
- [Kakute F7 Mini V3](boards/kakute-f7-mini-v3.md)
- [GEPRC GEP-F722-HD V2](boards/geprc-f722-v2.md)
+53
View File
@@ -24,6 +24,59 @@ The data channel owns rate selection, mailbox consumption, message encoding,
and bounded TX classification. Board code supplies physical routes and
telemetry configuration; it does not receive generic output authority.
Configuration and runtime observability are separate, one-way planes:
```text
Kaptan GCS custom MAVLink V2 messages
│ (future: verified signing, source binding, replay state)
▼
kaptan-config-mavlink ──► state-only ConfigProtocol
│ borrows
kaptan-flight-runtime
├── one ConfigManager ──► kaptan-config ──► flight schema/CBOR
└── one kaptan-log hub
│
kaptan-log-mavlink payload adapter
│
platform-owned sole encoder / DMA TX link
```
The generated dialect is intentionally custom rather than a `PARAM_*` or QGC
adapter. The core uses numeric field IDs and typed scalar values; descriptor
text is metadata, not a wire identity. Discovery/value reads, bounded
descriptor pages, and structured results are custom-dialect records. Runtime
logs use one bounded text message with MAVLink severity and flags. CBOR import uses a fixed
4 KiB transaction buffer and
fixed 192-byte chunks; its decoded candidate still goes through the same whole
profile validation path. The core owns one RAM transaction and the distinction
between boot-active, committed, and editable candidate snapshots. In the
current P1 policy a commit is restart-only. It cannot mutate controller or
estimator state in flight.
One current CBOR profile format is a bounded complete snapshot. There is no
migration reader: any other format is rejected. SITL supplies the host file
adapter and persists a nonzero generation alongside its raw profile payload.
Motor allocation/mixer topology is immutable compiled
vehicle capability data, never a scalar field or CBOR candidate member. The
bare-metal receiver continues to reject inbound control authority. Target
persistence, authenticated ingress, durable audit logging, disarmed
activation, and atomic consumer reconstruction remain separate reviewed work
and cannot be implied by a valid CBOR file or CRC.
The MAVLink dialect root includes `kaptan/config.xml` and `kaptan/log.xml`.
The build watches every fragment and generates one `message::kaptan` module;
fragments are never separately generated Rust modules. `kaptan-log` itself has
no MAVLink, platform, time, allocation, filesystem, or configuration
dependency. It assigns bounded Critical and BestEffort queues for text logs.
The platform drains a record only after TX admission; the diagnostic wire
contract deliberately contains only severity, flags, bounded text length, and
text. Local saturation accounting remains inside the hub, facade, and producer
mailboxes; heartbeat and safety telemetry remain separate from diagnostic text.
See [Configuration](configuration.md) for the deployment matrix and GCS upload
contract, [Logs](logs.md) for the receiver contract, and [Errors](errors.md)
for derived-error and failure-reporting scope.
## Ownership rules
- A peripheral owner is local to the platform runtime.
+6
View File
@@ -3,6 +3,12 @@
The GEPRC GEP-F722-HD V2 board declaration uses the ICM-42688-P on SPI3 and
MAVLink telemetry on USART2. It declares no vertical sensor stream.
Its immutable board policy selects the ICM-42688-P yaw-180° mount, logical
MAVLink `Serial2`, endpoint `1/1`, 115200 baud, the documented telemetry
schedule, and an `INFO` optional-log threshold. The platform verifies these
values against the checked SPI3/USART2 typestate route before it enables
peripherals. They are compiled image policy, not ground-station parameters.
Build and package from the workspace root:
```sh
+6
View File
@@ -5,6 +5,12 @@ MAVLink telemetry on USART1, and the MTF01 range/optical-flow route on UART4.
The checked-in manufacturer datasheet is
`boards/kakute-f7-mini-v3/datasheet.pdf`.
Its immutable board policy selects the MPU-6000 body-aligned mount, logical
MAVLink `Serial1`, endpoint `1/1`, 115200 baud, the documented telemetry
schedule, and an `INFO` optional-log threshold. The platform verifies these
values against the checked SPI1/USART1/MTF01 typestate route before it enables
peripherals. They are compiled image policy, not ground-station parameters.
## Build and package
From the workspace root:
+102
View File
@@ -0,0 +1,102 @@
# Configuration
Kaptan has one generic configuration lifecycle crate, `kaptan-config`. It
owns descriptors, typed writes, transactions, validation, generations, and
stores. It does not grow a crate per subsystem. Flight-specific values live in
the single `kaptan-flight-config` candidate; board electrical topology remains
in the immutable board declaration.
## Current deployment matrix
| Deployment | Configuration consumed today | Remote writes |
| --- | --- | --- |
| `kaptan-sitl serve --config FILE.cbor` | One validated Quad-X profile at process construction. | No live MAVLink endpoint is attached yet. |
| `kaptan-sitl serve` | The validated factory Quad-X profile. | No persistent store; commits are rejected. |
| STM32F722 telemetry image | Immutable compiled `BoardConfig` plus checked typestate routes. | Disabled: all inbound MAVLink remains discarded. |
The current SITL profile has only settings consumed by its estimator,
controller, safety supervisor, allocator envelope, or log hub:
- prearm dwell;
- IMU, local-aiding, and command freshness/timing;
- position/velocity aiding variance;
- outer-loop gains and acceleration limits;
- mass, collective-thrust, and tilt limits;
- rate PID gains and limits;
- actuator minimum, maximum, and slew limits; and
- the restart-only maximum optional log severity.
The fixed Quad-X mixer is intentionally not configurable. It is reviewed
capability data, not an operator-editable array.
Each editable field declares its ID, display key, unit, default, scalar type,
and local validation in one `#[config(...)]` attribute. The derive generates
the descriptor registry and local validation from that declaration; the only
handwritten checks are whole-profile physical relationships such as thrust
margin, timing ordering, and actuator limits.
## Immutable F722 board policy
The checked-in F722 boards consume these compiled `BoardConfig` settings before
peripheral initialization:
- IMU model and its fixed body mount (`BodyAligned` or `Yaw180`);
- logical MAVLink port, endpoint IDs, and baud rate;
- vertical-sensor capability and telemetry schedule; and
- maximum optional MAVLink log severity.
The platform compares that metadata with the selected `F722BoardRoutes` before
claiming pins, UARTs, DMA, or IRQs. A board declaration therefore cannot claim
an MPU-6000 while selecting the ICM route, select the wrong serial port or
mount, or request a baud rate that cannot carry the fixed link budget. The
physical port, sensor driver, pins, DMA, timers, and calibration are not file
configuration and are not GCS-editable.
F722’s log threshold is immutable board policy. It is deliberately separate
from the SITL profile’s `observability.maximum_log_level`: the F722 image does
not construct a runtime `ConfigManager` or profile store.
## One CBOR profile format
There is one unreleased raw profile blob format:
- it is a CBOR map keyed by the discovered numeric `FieldId`s;
- key `0` is `profile_format = 1` and reserved key `65535` is the 16-byte
`schema_id = "KAPTANPROFILE-01"`;
- the complete blob is at most 4 KiB; and
- malformed, trailing, wrong-format, non-finite, out-of-range, cross-field,
and capability-invalid values are rejected.
The map is exact: it has those 26 keys once each. Unknown, duplicate, missing,
or indefinite-map keys are rejected. That keeps the unreleased file contract
unambiguous instead of silently accepting undeclared extensions.
There is no migration path because no version has shipped. The raw blob is the
future GCS import/export representation. The SITL file is a
private host envelope `[generation, raw-profile-blob]`; it is not an upload
blob and must not be sent to `IMPORT_*` directly.
## Future GCS upload contract
The custom dialect is prepared for a first-party GCS without borrowing
`PARAM_*` semantics. A client will:
1. send `KAPTAN_CONFIG_INFO_REQUEST` and require the exact protocol, profile
format, and schema ID in the correlated `KAPTAN_CONFIG_INFO` reply;
2. request descriptors and values as needed;
3. open a transaction with `KAPTAN_CONFIG_BEGIN`;
4. either issue typed setters or begin a raw-CBOR upload with the advertised
schema ID, then send aligned idempotent chunks in any order;
5. validate and commit; and
6. treat `COMMITTED_RESTART_REQUIRED` as persistence only, never live
activation.
An accepted import begin returns a server-issued transfer ID. Every chunk and
finish request echoes it, preventing delayed data from an earlier transfer
from entering the current one. The service validates CRC-32C and the entire
candidate before it replaces the RAM transaction.
This is protocol groundwork, not a claim of active aircraft configuration:
F722 needs authenticated ingress, source binding and replay protection,
journalled target storage, a disarmed/inhibited activation permit, and atomic
consumer rebuild/reset before it may accept any configuration command.
+47
View File
@@ -0,0 +1,47 @@
# Errors
Public Kaptan error enums derive the pure `KaptanError` implementation:
```rust
#[derive(Debug, kaptan_log::KaptanError)]
enum SensorError {
TransferFault,
UnexpectedIdentity(u8),
}
```
The derive implements `core::error::Error`, `Display`, and `ErrorReport`. It
creates a bounded human-facing `module::Type::Variant` message without
allocating, publishing telemetry, exposing generic source values, or changing
safety state. Renaming a module, type, or variant changes that text; it is not
a stable machine protocol identity.
Normal-task code may use `log::error!("{error}")`; it is routed as a bounded
`KAPTAN_ERROR`. ISR-adjacent flight paths may enqueue the derived static
message through their existing bounded mailbox. Neither route is a replacement
for safety state, actuator inhibit, or typed configuration results.
## What the receiver sees
- Configuration writes receive authoritative structured failures through
`KAPTAN_CONFIG_RESULT`: operation, lifecycle state, field IDs, validation
rule, and protocol error code.
- Runtime driver and link faults reach the F722 MAVLink worker as
`KAPTAN_ERROR` text, subject to bounded transport admission. The message
carries no per-error numeric catalog: a `KaptanError` derive supplies its
type/variant display identity.
- A rejected configuration operation may enqueue a companion `KAPTAN_ERROR`
containing the high-level `ProtocolError` identity. It is intentionally not
correlated and carries no field/rule detail; the matching
`KAPTAN_CONFIG_RESULT` remains the sole precise authority.
- Control, navigation, safety, and simulator error types are derive-ready but
their live owners are not yet attached to a MAVLink worker.
- `KAPTAN_SHUTDOWN` represents only a future completed orderly,
output-inhibited shutdown. It is not a shutdown request, panic, hard-fault,
reset, or retention-record protocol. F722 still uses `panic-halt`; it does
not claim a live panic frame or retained-record replay.
No diagnostic message may synchronously transmit from a panic handler, hard
fault, ISR, DMA callback, or motor-inhibit path. A later target integration
must inhibit outputs first, persist a validated crash record, then report it
only after the next boot’s static board telemetry is available.
+8 -3
View File
@@ -14,6 +14,10 @@ Markdown.
- New contributors should read [Getting started](getting-started.md).
- Board work belongs under [Boards](boards/index.md).
- The currently supported closed-loop simulation is [Host SITL](sitl.md).
- [Configuration](configuration.md) distinguishes the current SITL profile
from immutable F722 board policy.
- [Logs](logs.md) and [Errors](errors.md) describe the custom MAVLink
observability surface and its current safety scope.
- Before submitting changes, follow [Development and verification](development.md).
## Documentation conventions
@@ -22,6 +26,7 @@ Documentation is centralized in `docs/src/`; source directories do not carry
README files. Keep pages in Markdown, add each page to `SUMMARY.md`, and place
commands beside the assumptions and safety boundary they depend on.
`TODO(configuration)` marks a required configuration contract that has not
been implemented. It is not permission for an operator to substitute runtime
values or bypass an immutable build-time safety decision.
The current configuration contract is intentionally deployment-specific. A
SITL Quad-X profile is a bounded restart-only CBOR snapshot; F722 hardware
selection remains immutable board policy. Neither is permission for an
operator to substitute runtime values or bypass an immutable safety decision.
+61
View File
@@ -0,0 +1,61 @@
# Logs
Normal host/SITL task code can use the standard facade:
```rust
log::debug!("estimator waiting for IMU");
log::info!("configuration loaded");
log::warn!("range aid stale");
log::error!("sensor transfer failed"); // emitted as KAPTAN_ERROR
```
`kaptan-log` formats once into a fixed bounded buffer, queues locally, and
never sends MAVLink, allocates, blocks, changes flight state, or touches DMA.
The sole MAVLink worker drains admitted records. F722 deliberately does not
link this facade in ISR, DMA, control, actuator, panic, hard-fault, or shutdown
contexts; those paths use static fault mailboxes that the worker turns into
messages later.
## Receiver contract
`definitions/kaptan/log.xml` separates ordinary logs from errors:
```text
KAPTAN_LOG { level, flags, message_length, message[160] }
KAPTAN_ERROR { flags, message_length, message[160] }
KAPTAN_SHUTDOWN { reason }
```
A receiver generated from the same `kaptan.xml` revision validates
`message_length <= 160` and decodes exactly that UTF-8 prefix. `KAPTAN_LOG`
displays its text at the MAVLink severity in `level`; `TRACE` is encoded as
`DEBUG` plus the `TRACE` flag. `KAPTAN_ERROR` is error-only and accepts only
`TRUNCATED` and `COALESCED`; a `TRACE` bit is never emitted there. Unknown
future flag bits must be ignored.
The derived error display text is intentionally not a numeric error catalog;
renaming its Rust module/type/variant changes the display identity. Error
details requiring machine action stay in their owning protocol, notably
`KAPTAN_CONFIG_RESULT`. `KAPTAN_SHUTDOWN` has only its typed reason and is
valid only after an orderly output-inhibited completion. Delivery is bounded
and lossy under link pressure; heartbeat and safety telemetry use separate
paths.
## Filtering
The SITL profile’s restart-only `observability.maximum_log_level` accepts
MAVLink severities Error (3) through Debug (7). For example, selecting Warning
permits warning records while suppressing info and debug records. Error-level
records always use `KAPTAN_ERROR` and remain non-filterable. F722 uses its
immutable board log threshold instead, because it has no active runtime
profile.
Cargo `log/max_level_*` features compile calls away before the facade sees
them; they are not a supported filtering mechanism. The build policy keeps
that decision out of workspace dependencies.
The tested `SitlConfigRuntime` composition proves `log!` reaches a generated
`KAPTAN_LOG` payload, and rejected configuration replies enqueue a generated
`KAPTAN_ERROR` companion without replacing their authoritative result. `kaptan-sitl
serve` does not yet attach a config/diagnostic MAVLink link, and F722 emits only
outbound telemetry/diagnostic frames while rejecting all inbound authority.
+9
View File
@@ -18,3 +18,12 @@ Telemetry transmission uses bounded frame classes. A critical frame may
replace pending best-effort traffic, but never the DMA-owned active buffer.
Completion retries preserve ordering and retain a transiently busy frame for a
later service turn.
The custom Kaptan configuration and log definitions are aggregated by
`definitions/kaptan.xml` from `kaptan/config.xml` and `kaptan/log.xml`.
`KAPTAN_LOG` is an outbound human-readable four-field record. `KAPTAN_ERROR`
is its separate bounded error channel, while `KAPTAN_SHUTDOWN` represents only
a completed orderly output-inhibited shutdown. Configuration messages are GCS
groundwork only: the current F722 receiver rejects every inbound frame before
configuration dispatch. See [Configuration](configuration.md), [Logs](logs.md),
and [Errors](errors.md) for the exact contracts.
+23 -12
View File
@@ -1,19 +1,30 @@
# Reference
## Build-time configuration
This page collects stable limits and points to the detailed contracts.
Board configuration is immutable in the compiled image. Runtime parameter
updates and ground-station tuning are not exposed by the checked-in firmware.
| Surface | Current contract |
| --- | --- |
| Flight profile | One 4 KiB-or-smaller raw CBOR blob, `profile_format = 1`, schema ID `KAPTANPROFILE-01`. |
| SITL file | Private `[generation, raw-profile-blob]` envelope; not a MAVLink upload blob. |
| Config transaction | One owner-bound RAM transaction; typed writes or 192-byte idempotent CBOR chunks; commit is restart-required. |
| Config discovery | Correlated `KAPTAN_CONFIG_INFO_REQUEST` / `KAPTAN_CONFIG_INFO`, then descriptor/value requests. |
| Diagnostics | `KAPTAN_LOG` (51020) for ordinary severity/text, `KAPTAN_ERROR` (51021) for bounded errors, and `KAPTAN_SHUTDOWN` (51022) for completed orderly output-inhibited shutdowns. |
| F722 configuration | Immutable compiled board policy only; inbound MAVLink configuration remains disabled. |
`TODO(configuration)`: a future configuration system must validate and stage
an entire profile while disarmed, commit it atomically, and prove that outputs
remain inhibited throughout staging, activation, and power-loss recovery.
Read [Configuration](configuration.md) for the field/consumer matrix and GCS
upload sequence, [Logs](logs.md) for receiver decoding and filtering, and
[Errors](errors.md) for error propagation and panic scope.
## Simulation package reproducibility
## Verification
The host SITL Docker build uses digest-pinned base images and the locked Rust
dependency graph. The signed Gazebo APT feed is not snapshot-pinned.
Before review, run:
`TODO(reproducibility)`: snapshot/version-pin the Gazebo package set and
verify the repository signing-key fingerprint before treating the simulator
image as byte-for-byte reproducible release input.
```sh
cargo fmt --all --check
cargo test --workspace --locked --offline
cargo clippy --workspace --all-targets --locked -- -D warnings
```
The embedded no-std binaries require their target-specific build setup; the
host workspace test suite continues to assert that the telemetry F722 image
has no inbound authority or actuator path.
+18
View File
@@ -32,6 +32,24 @@ never contributes a controller or truth observation.
Open <https://app.gazebosim.org/visualization>, connect to
`ws://localhost:9002`, and leave authorization blank to view the world.
## Quad-X profile
SITL starts through the same central `ConfigManager` composition used by the
host protocol tests. Create and validate a private host profile file with:
```sh
kaptan-sitl config write-default kaptan-sitl.cbor
kaptan-sitl config validate kaptan-sitl.cbor
kaptan-sitl serve --config kaptan-sitl.cbor
```
The file selects the restart-only profile used to build the estimator,
controller, allocator envelope, safety supervisor, and optional log filter at
process start. It is not the raw GCS upload blob. `kaptan-sitl serve` retains
the central manager for that boot but does not yet open a MAVLink config/log
endpoint; see [Configuration](configuration.md) for the exact upload groundwork
and safety boundary.
## Limits
This model does not validate terrain, vision, flow/range sensing, wind,