Skip to content

Plugin SDK: notify gateway of entity-surface changes + manifest reload #376

@bburda

Description

@bburda

Problem

Plugins that implement UpdateProvider (or any other plugin that mutates the entity surface at runtime) currently have no way to tell the gateway "my entities changed, please refresh your tree". The gateway's entity tree is populated from:

  1. The manifest file, loaded once at startup.
  2. Runtime discovery, which picks up ROS 2 graph changes on a polling interval.

When a plugin deploys a new app (e.g. drops a Python node into /opt/app-root/addons/<id>/main.py and starts it) the runtime discovery catches the new node within a few seconds, but:

  • It shows up as an orphan (not in manifest), so it can't be attached to a parent component.
  • There's no way for the plugin to ship a matching manifest fragment at deploy time and have the gateway pick it up.
  • After the plugin deletes the app (e.g. rollback), the manifest entry — if it ever existed — is never cleared.

This is a gap for any plugin whose job is to mutate the entity surface: OTA installers, rosbag injectors, dynamic config deployers, hot-reloadable adapters.

Proposal

Two parts:

1. PluginContext::notify_entities_changed(scope)

Add to ros2_medkit_gateway::PluginContext a method plugins call when they have finished mutating entities:

struct EntityChangeScope {
  std::optional<std::string> area_id;
  std::optional<std::string> component_id;
  // empty/null = full refresh
};

virtual void notify_entities_changed(const EntityChangeScope & scope) = 0;

The gateway responds by:

  • Re-reading the manifest file from disk (cheap, just a single yaml parse).
  • Scanning a well-known fragments directory (new, see part 2).
  • Re-running runtime discovery for the affected scope.
  • Merging the fresh result into the entity cache via the existing merge_pipeline.

Existing ResourceChangeNotifier stays as-is — it's for resource-item changes (faults, data, config), not entity-surface changes. The two concerns are separate.

2. Manifest fragments directory

Introduce a gateway parameter manifest.fragments_dir (default: empty = disabled). When set, the manifest loader scans that directory for *.yaml / *.yml files at every manifest load and merges them on top of the base manifest. Fragment schema is a strict subset of the main manifest (apps / functions / subcomponents only; no new areas or top-level components without allow_manifest_override: true).

This gives plugins a place to drop per-deploy manifest chunks alongside their deployed files. Removing the fragment (during plugin's cleanup / rollback) + calling notify_entities_changed makes the entry disappear.

Non-goals

  • No hot-reload of plugin binaries.
  • No change to SSE / trigger / websocket APIs (use existing ResourceChangeNotifier for resource-level events).
  • No new HTTP endpoint (the gateway's admin surface is and should remain SOVD-only).

Compatibility

  • PluginContext is a versioned interface (PLUGIN_API_VERSION). This change bumps the API version and gets a default no-op implementation for backwards compatibility with plugins built against the previous header.
  • manifest.fragments_dir defaults to empty, so no behavior change for users who don't opt in.

Acceptance criteria

  • New EntityChangeScope type and PluginContext::notify_entities_changed method.
  • Gateway implementation re-parses manifest + runs discovery for the given scope.
  • New manifest.fragments_dir parameter with fragment loader.
  • Unit tests covering: full-surface refresh, scoped refresh (by area, by component), fragment merge, fragment removal after refresh.
  • Integration test: a test plugin drops a fragment file, calls notify_entities_changed, the new app appears in GET /apps; removes the fragment + notifies, the app disappears.
  • Docs: docs/design/ page describing the lifecycle, extended CHANGELOG, PLUGIN_API_VERSION bump.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions