Skip to content

Registration-plugin contract matrix (2026-08-24)

This is a source/test audit of the versioned registration boundary used by the scanmatcher frontend and graph backend/offline shell. It is not a Humble or Jazzy runtime result and does not promote the external DSO path. “Strong static” means the current source and synthetic/unit tests establish the invariant without a distro container; “partial” records a deliberate remaining contract gap.

Requirement Current implementation Evidence Status / remaining gap
Stable API and ABI identity ApiVersion, runtime descriptor, ABI epoch, compiler/libstdc++ tag, and interface-contract digest in registration.hpp; loader validates the sidecar before construction and the provider descriptor after configuration test_registration_plugin_loader contract-manifest tests plus provider/source audits Strong static, runtime NOT_READY: the contract scope is explicit, but a raw header/interface digest is not a complete C++ ABI proof and distro/toolchain rebuild evidence remains required.
Exact major/minor and capability negotiation isApiCompatible() requires equal major and plugin minor no newer than host; CapabilityRequirements validates every requested bit/policy/metric loader capability/API tests; scanmatcher/backend preflight helpers Strong static for declared fields. Unknown future fields and explicit ABI identity are not negotiated.
Class, DSO, and plugin-XML provenance class ID must match metadata and provider; install-time sidecar binds canonical XML/DSO size and SHA-256, class, schema, API range, capabilities, policies, config schema, and manifest digest readAndValidateRegistrationContractManifest, symlink/missing/byte-drift tests, post-construction descriptor drift tests Strong static, installed-prefix and distro-specific ELF/ODR/load evidence still NOT_READY.
Loader lease, lifetime, and unload RegistrationPluginSession owns the pluginlib ClassLoader before the plugin pointer; host-adopted PCL objects use the same session lease; transaction destruction/reset order is explicit external-session lease tests; host-session adoption; activation commit/rollback tests Strong static. No distro runtime unload smoke has been completed.
Transactional prepare/validate/commit/rollback host-owned slots are untouched until validation; commit swaps plugin before session; rollback rejects unexpected concurrent mutation activation reject, validator-throw, concurrent-mutation, commit/rollback tests Strong static. External DSO constructor/static-initializer side effects remain outside the rollback boundary.
Constructor, resource-init, processing, and destructor exceptions loader catches discovery/create/metadata/config exceptions; startup resource failure rolls back; session wrappers catch setInputTarget/align exceptions and latch fault loader constructor/config tests; resource-init tests; session processing exception tests Strong static for declared boundaries. A plugin violating reset() noexcept or throwing from its destructor terminates by the C++ contract; it cannot be recovered by the host.
Thread safety, reentrancy, and ownership session serializes kSerializedOwner; kReentrant is allowed to run concurrently; an in-flight counter and condition-variable barrier prevent reset/unload during callbacks; session owns processing state and the DSO lease bounded C++14 stress harness (non-TSAN/TSAN modes), source guards, loader concurrency tests Strong static, stress evidence when the dedicated test is run: kCooperativeCancel and kNonInterruptibleAlign are explicit; a non-interruptible provider may delay shutdown until its current call returns.
Deterministic configuration schema ParameterMap is typed and ordered (std::map); provider and sidecar bind schema ID/version/SHA before publication interface parameter tests; synthetic contract-manifest and descriptor-drift tests Strong static for declared schema identities; schema bytes and distro runtime installation remain NOT_READY.
Cancellation and shutdown cancel() blocks future admission and performs pre/post checkpoints; cooperative providers receive requestCancel(); idempotent shutdown() waits for quiescence before reset() noexcept bounded stress harness covers cancel-before/during/after, provider observation, repeated shutdown, fault latch, and timeout bounds Strong static with bounded stress characterization. PCL built-ins remain kNonInterruptibleAlign: cancellation cannot hard-interrupt an active call and shutdown may wait for it to return.
No host-state leak startup transaction keeps candidate state private; no ROS resources are exposed before commit; failures preserve the previous host slots; built-in resource-init failure rolls back the adopted session activation and scanmatcher injection/resource-init tests; source boundary audit Strong static for host-owned state; external static initialization and arbitrary DSO global side effects are explicitly not rollbackable.
Backward/forward compatibility API major/minor rule rejects incompatible major/newer plugin minor; appended failure code preserves existing enum values; old raw accessor remains source-compatible API compatibility tests; loader mismatch tests Partial: no ABI identity, config-schema negotiation, or explicit capability-version policy for forward extensions.
External C++14 SDK/install surface interface is ROS-free/header-level C++14; consumer/template checks bind source manifest and install metadata run_registration_plugin_consumer_check.sh, template check, matrix audit Strong static. Humble/Jazzy clean install/build/load and external DSO runtime evidence remain NOT_READY.
Observability and failure reason LoadFailure has stable reason codes/messages; alignment diagnostics carry detail; wrapper exceptions become structured internal/cancelled failures loader negative tests; scanmatcher failure-name mapping; wrapper tests Strong static. Production log/receipt schema for processing fault transitions is not yet a distro-runtime artifact.

Wiring boundary

Every supported live scanmatcher selector (NDT, GICP, and dependency-enabled FAST/SMALL variants) is now host-resolved into a typed adapter and committed through RegistrationPluginSession; the historical PCL defaults remain in their same translation unit and parameter maps. Live and offline graph NDT and GICP use the same session boundary. The GICP bridge is an explicitly preconfigured host adoption, never an implicit pluginlib fallback.

BackendCore retains its ROS-free RegistrationPlugin& API, but live/offline shells pass a RegistrationPluginSessionAdapter, so target and alignment calls still use the lease, serialized ownership, exception latch, cancellation, and idempotent shutdown semantics. The source auditor scripts/audit_registration_plugin_processing_boundary.py rejects raw align, target, source, convergence, and fitness calls in the three production shells. The standalone small_gicp_odom_node and map_ndt_residual_report_main are explicit, separately documented analysis / odometry seams and are not live scanmatcher/backend consumers.

Runtime disposition

The current runtime status is NOT_READY. No Humble/Jazzy container, live sensor run, bag replay, benchmark, map, ground-truth, scorer, or external-DSO promotion is inferred from this static slice. The remaining release blockers are distro-specific C++14 consumer/install proof, ABI identity policy, plugin-declared configuration schema identity, dependency-enabled DSO/ODR runtime evidence, and Humble/Jazzy execution of the dedicated stress test.