| name | external-source-adapter |
| description | Write or review Scenescape external-source converter/adapter scripts that map native telemetry (MAVLink, ROS 2, NMEA, CAN, UWB/RTLS, proprietary) into the Scene Controller external_source MQTT contract. Use when the task involves external sources, publishers, adapters, converters, source_id, or scenescape/external topics. |
External-Source Adapter Skill
Help a user or agent write a small converter that publishes into
scenescape/external/{publisher_id}/{thing_type} with source_id equal to
{publisher_id} (publisher-centric; scenes bind separately).
Mandatory Reads (In Order)
Before writing or changing converter code or docs:
- This skill.
- User guide:
docs/user-guide/how-to-guides/publish-external-source-adapter.md
- Canonical contract:
docs/user-guide/microservices/controller/data_formats.md
(section External Source Input Message Format and its subsections).
- When validating payloads against schema definitions:
controller/src/schema/metadata.schema.json
(external_source, external_pose, external_detection).
- If an example adapter already exists for the protocol or a close cousin, read
it before inventing a new layout:
tools/external_source_adapters/ (for example mavlink_to_external_source.py).
Architecture background (optional):
docs/adr/0016-unified-external-source-ingestion.md.
Hard Rule — No Contract Duplication
The field tables, required-field lists, pose-trust rules, identity/collision
guidance, rejection reasons, and full JSON examples are defined only in
data_formats.md (backed by the schema).
- Do not copy those tables, field lists, or examples into this skill, into
generated code comments, or into new documentation pages.
- Cite the relevant
data_formats.md anchors (or the how-to checklist) instead.
- If the contract and a snippet appear to disagree, trust
data_formats.md /
the schema and fix the snippet.
Preferred Layout
- Put runnable example adapters under
tools/external_source_adapters/.
- Keep protocol-specific dependencies in that directory's
requirements.txt
(for example pymavlink). Do not add them to core Scenescape runtime
requirements unless the product explicitly adopts the protocol.
- Follow the how-to's
SCENESCAPE_* environment-variable naming for source id,
broker, MQTT auth, and root cert. Topic path uses SCENESCAPE_SOURCE_ID.
- Reuse
scene_common.mqtt.PubSub and PubSub.DATA_EXTERNAL rather than a
one-off MQTT client, unless the user requires otherwise.
- When adding or renaming an example adapter, update
tools/external_source_adapters/README.md and add a See Also link from the
how-to — do not paste the script into data_formats.md.
Agent Checklist When Writing a Converter
- Publish to
external/{source_id}/{thing_type} with matching payload
source_id. Do not put a scene uid in the topic path. Prefer wgs84 for
mobile agents (geospatial auto-attach); use
CONTROLLER_EXTERNAL_SOURCE_BINDINGS for scene-frame poses.
- Choose a persistent
source_id and per-object id (see Choosing a
source_id in data_formats.md); never mint fresh UUIDs on each restart.
- Map native observations into source-local
translation (metres) relative to
the source origin described by pose.
- Choose
reference_frame (wgs84 vs scene) correctly; honor
CONTROLLER_TRUSTED_POSITIONING_SOURCES for scene poses.
- Convert the source's native orientation (euler, DCM, etc.) into the
contract's quaternion
(x, y, z, w). Do not invent alternate rotation
field shapes.
- Use ISO 8601 UTC timestamps (
scene_common.timestamp.get_iso_time is fine).
- Pose-only sources (GNSS/attitude with no separate detections): either
publish
pose with an empty objects array (cache refresh), or report the
platform itself as one object at [0, 0, 0] using the same persistent id as
source_id — see Choosing a source_id in data_formats.md. Do not mint a
second identity for "the vehicle."
- Publish over authenticated MQTT with the Scenescape CA cert (see the how-to
skeleton using
scene_common.mqtt.PubSub and PubSub.DATA_EXTERNAL).
- Keep credentials in environment variables or secret files — never hard-code
and never ship default passwords.
- Do not add controller-side ID remapping, lookup caches, or identity
allowlists; translation belongs in the adapter.
- Prefer linking readers to
data_formats.md examples over inventing new
sample payloads.
Out of Scope (Refuse / Defer)
Do not implement or imply as part of an adapter task:
- Footprint handoff / overlap policy for the spatial binder (ADR 14 Future Work)
- Cross-source fusion or camera/external deduplication
- Per-source identity trust allowlists (identity is trusted by default with
collision detection — see
data_formats.md)
- Changes to Scene Controller pose cache, identity registry, or schema unless
the user explicitly requested a contract change
- Broker mTLS/ACL binding of credentials to publisher ids, or other trust-domain
hardening beyond same-authority certs (ADR 14 Future Work)
- Promoting a protocol library into core Scenescape dependencies "for
convenience"
Maintenance (Anti-Drift)
When the external-source contract changes:
- Update
controller/src/schema/metadata.schema.json and the External Source
section of docs/user-guide/microservices/controller/data_formats.md first.
- Then only adjust broken links, checklist wording, or the how-to MQTT skeleton
in
docs/user-guide/how-to-guides/publish-external-source-adapter.md and
this skill.
- If example adapters under
tools/external_source_adapters/ break against the
new contract, fix their mapping code and README pointers — still without
copying field tables into the README.
- Do not re-document fields here. This skill stays a thin procedure +
pointer document.