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.
1---2name: external-source-adapter3description: 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.4---56# External-Source Adapter Skill78Help a user or agent write a small converter that publishes into9`scenescape/external/{publisher_id}/{thing_type}` with `source_id` equal to10`{publisher_id}` (publisher-centric; scenes bind separately).1112## Mandatory Reads (In Order)1314Before writing or changing converter code or docs:15161. This skill.172. User guide:18 `docs/user-guide/how-to-guides/publish-external-source-adapter.md`193. Canonical contract:20 `docs/user-guide/microservices/controller/data_formats.md`21 (section **External Source Input Message Format** and its subsections).224. When validating payloads against schema definitions:23 `controller/src/schema/metadata.schema.json`24 (`external_source`, `external_pose`, `external_detection`).255. If an example adapter already exists for the protocol or a close cousin, read26 it before inventing a new layout:27 `tools/external_source_adapters/` (for example `mavlink_to_external_source.py`).2829Architecture background (optional):30`docs/adr/0016-unified-external-source-ingestion.md`.3132## Hard Rule — No Contract Duplication3334The field tables, required-field lists, pose-trust rules, identity/collision35guidance, rejection reasons, and full JSON examples are defined only in36`data_formats.md` (backed by the schema).3738- Do **not** copy those tables, field lists, or examples into this skill, into39 generated code comments, or into new documentation pages.40- Cite the relevant `data_formats.md` anchors (or the how-to checklist) instead.41- If the contract and a snippet appear to disagree, trust `data_formats.md` /42 the schema and fix the snippet.4344## Preferred Layout4546- Put runnable example adapters under `tools/external_source_adapters/`.47- Keep protocol-specific dependencies in that directory's `requirements.txt`48 (for example `pymavlink`). Do **not** add them to core Scenescape runtime49 requirements unless the product explicitly adopts the protocol.50- Follow the how-to's `SCENESCAPE_*` environment-variable naming for source id,51 broker, MQTT auth, and root cert. Topic path uses `SCENESCAPE_SOURCE_ID`.52- Reuse `scene_common.mqtt.PubSub` and `PubSub.DATA_EXTERNAL` rather than a53 one-off MQTT client, unless the user requires otherwise.54- When adding or renaming an example adapter, update55 `tools/external_source_adapters/README.md` and add a See Also link from the56 how-to — do not paste the script into `data_formats.md`.5758## Agent Checklist When Writing a Converter59601. Publish to `external/{source_id}/{thing_type}` with matching payload61 `source_id`. Do not put a scene uid in the topic path. Prefer `wgs84` for62 mobile agents (geospatial auto-attach); use63 `CONTROLLER_EXTERNAL_SOURCE_BINDINGS` for `scene`-frame poses.642. Choose a persistent `source_id` and per-object `id` (see Choosing a65 `source_id` in `data_formats.md`); never mint fresh UUIDs on each restart.663. Map native observations into source-local `translation` (metres) relative to67 the source origin described by `pose`.684. Choose `reference_frame` (`wgs84` vs `scene`) correctly; honor69 `CONTROLLER_TRUSTED_POSITIONING_SOURCES` for `scene` poses.705. Convert the source's native orientation (euler, DCM, etc.) into the71 contract's quaternion `(x, y, z, w)`. Do not invent alternate rotation72 field shapes.736. Use ISO 8601 UTC timestamps (`scene_common.timestamp.get_iso_time` is fine).747. **Pose-only sources** (GNSS/attitude with no separate detections): either75 publish `pose` with an empty `objects` array (cache refresh), or report the76 platform itself as one object at `[0, 0, 0]` using the same persistent id as77 `source_id` — see Choosing a `source_id` in `data_formats.md`. Do not mint a78 second identity for "the vehicle."798. Publish over authenticated MQTT with the Scenescape CA cert (see the how-to80 skeleton using `scene_common.mqtt.PubSub` and `PubSub.DATA_EXTERNAL`).819. Keep credentials in environment variables or secret files — never hard-code82 and never ship default passwords.8310. Do **not** add controller-side ID remapping, lookup caches, or identity84 allowlists; translation belongs in the adapter.8511. Prefer linking readers to `data_formats.md` examples over inventing new86 sample payloads.8788## Out of Scope (Refuse / Defer)8990Do not implement or imply as part of an adapter task:9192- Footprint handoff / overlap policy for the spatial binder (ADR 14 Future Work)93- Cross-source fusion or camera/external deduplication94- Per-source identity trust allowlists (identity is trusted by default with95 collision detection — see `data_formats.md`)96- Changes to Scene Controller pose cache, identity registry, or schema unless97 the user explicitly requested a contract change98- Broker mTLS/ACL binding of credentials to publisher ids, or other trust-domain99 hardening beyond same-authority certs (ADR 14 Future Work)100- Promoting a protocol library into core Scenescape dependencies "for101 convenience"102103## Maintenance (Anti-Drift)104105When the external-source contract changes:1061071. Update `controller/src/schema/metadata.schema.json` and the External Source108 section of `docs/user-guide/microservices/controller/data_formats.md` first.1092. Then only adjust broken links, checklist wording, or the how-to MQTT skeleton110 in `docs/user-guide/how-to-guides/publish-external-source-adapter.md` and111 this skill.1123. If example adapters under `tools/external_source_adapters/` break against the113 new contract, fix their mapping code and README pointers — still without114 copying field tables into the README.1154. Do **not** re-document fields here. This skill stays a thin procedure +116 pointer document.