Extensible OSS architecture roadmap¶
Future-only systems direction (post-SOTA evidence phase)¶
After the current evidence phase and its independent SOTA gates are complete, future work is split into two tracks. Both tracks are exploratory and additive: they are not implemented here, make no present benchmark or SOTA claim, do not authorize replay/GT/scorer activity, and do not change the current SLAM default.
Track A — lifelong and long-term SLAM¶
Track A studies a durable map lifecycle rather than a new default estimator. A multi-session map would be a version DAG with session and submap provenance, explicit branch/merge and rollback semantics, and change classification for dynamic, seasonal, and structural changes. The lifecycle would also study bounded storage and compaction, catastrophic-forgetting detection, relocalization and recovery, fleet federation with conflict resolution, and privacy/security controls for retained observations and shared map state.
The differentiator is deliberately an evidence contract around map history, change decisions, recovery, and privacy—not a claim that the estimator is better than other OSS. Each capability below is a future gate with a declared dependency and time horizon; a missing dependency is a No-Go, never an implicit pass.
| Track A capability | User value | Technical capability to prove | Public dataset or self-contained protocol | KPI and Go/No-Go | Dependencies | Timing |
|---|---|---|---|---|---|---|
| Long-term change robustness | Keep localization useful across seasons, weather, construction, and dynamic-object turnover. | Classify dynamic/seasonal/structural change while preserving stable geometry and session provenance. | Repeated-route 24-hour/7-day simulation plus controlled weather, construction, and dynamic-object interventions. | False-change rate, localization availability, drift versus session age; Go only with predeclared bounds and crash-safe replay, otherwise No-Go. | Versioned observations, calibration identity, change-policy plugin, replay harness. | Mid-term |
| Multi-session merge and localization | Reuse prior maps and recover after disconnected or interrupted sessions. | Version-DAG branch/merge, provenance-aware relocalization, conflict detection, and deterministic rollback. | Public multi-session robotics sequences where available, plus a reproducible repeated-route protocol with injected branch conflicts. | Merge conflict loss, availability, relocalization time, drift, rollback determinism; any nondeterministic merge is No-Go. | Map schema/migrations, append-only event log, stable localization API. | Mid-term |
| Change detection and map aging | Prevent stale or transient observations from silently becoming permanent map state. | Age/retire evidence, distinguish transient from structural change, and expose reviewable map events. | Seasonal/revisit sequences and staged layout/roadway changes with held-out intervention schedules. | Structural-change precision/recall, false alarm rate, time-to-review, stale-map rate; missing provenance is No-Go. | Change classifier, policy plugin, session clock, review/export tooling. | Near-to-mid term |
| Bounded-memory map lifecycle | Keep long deployments operational under finite storage. | Crash-safe compaction, retention policies, submap eviction, and version-preserving recovery. | 7-day stress protocol with restart/crash injection and declared storage quota. | Storage growth bound, compaction loss rate, recovery time, byte-for-byte replay; quota breach or data loss is No-Go. | Versioned map store, transactional event log, filesystem durability contract. | Mid-term |
| Failure detection and rollback | Return to a known-good localization state after bad data, software, or storage events. | Detect divergence/catastrophic forgetting, select a known-good version, and restore host-owned state transactionally. | Fault-injection protocol covering process crash, corrupted event, rejected candidate, and sensor interruption. | Detection latency, recovery success/time, rollback determinism, zero partial publication; leaked state is No-Go. | Activation transaction, health/observability API, durable checkpoints. | Near-to-mid term |
| Fleet federation and privacy | Share useful map updates across robots without forcing raw-data centralization. | Conflict-aware cross-robot merge, bounded synchronization, access policy, provenance, and privacy-preserving retention. | Multi-robot intermittent-link protocol with divergent branches, delayed merges, and redacted/raw-data policy variants. | Cross-robot consistency, convergence time, bandwidth/storage bound, conflict loss, privacy-policy violations; violation is No-Go. | Federation transport, conflict resolver, key/identity policy, secure storage. | Long-term |
Track A is not exit-ready until a reproducible harness covers all of the following, with crash-safe replay and an auditable lineage for every result:
| Exit metric | Required future evidence |
|---|---|
| Long-duration operation | At least 24-hour and 7-day simulation or replay runs, with restart/crash injection. |
| Repeated-route aging | The same routes revisited across months and multiple sessions, with immutable session identities. |
| Localization availability | Availability and recovery distributions, including relocalization after map branch/rollback and sensor interruption. |
| Drift versus session age | Drift curves indexed by session age and map-version depth, with confidence bounds. |
| False change rate | Dynamic/seasonal/structural classification error, including false structural-change alarms. |
| Rollback determinism | Replaying the same event prefix produces the same selected version, map bytes, and localization result. |
| Storage growth bound | Measured growth, compaction safety, and a bounded-retention guarantee under the declared workload. |
| Recovery time | Time to restore a usable map/localization state after process, host, or storage failure. |
| Cross-robot consistency | Version/conflict convergence and localization agreement across independently operating robots. |
Future-only lifecycle invariants and candidate promotion targets¶
The Track A capabilities above are intentionally decomposed into invariants so that a future implementation cannot pass by reporting only average accuracy. The numerical values in this table are proposed planning targets, not current benchmark thresholds or SOTA evidence. They must be ratified in a versioned future profile before any acquisition, replay, GT, or scorer work; until then every row is NOT_READY. A missing, non-reproducible, or non-finite metric is an unconditional No-Go.
| Future invariant | Candidate KPI and minimum evidence | Go / No-Go | Explicit non-goal |
|---|---|---|---|
| Map-generation DAG and safe merge | 100% of sessions/submaps/events have immutable parent and source hashes; 100 deterministic replays produce identical lineage and map bytes; injected branch conflicts have 0 silently dropped updates. | Go only with append-only provenance, conflict receipts, and transactional merge/rollback; any orphan, silent overwrite, or nondeterminism is No-Go. | No automatic merging of unreviewed maps and no claim that a DAG alone improves accuracy. |
| Seasonal/time change detection | Report precision, recall, F1, false-structural-change rate, and time-to-review separately for dynamic, seasonal, and structural classes on a held-out intervention set; candidate target F1 >= 0.95 for structural changes and false-structural-change rate <= 0.02. | Go only when class labels, holdout identity, and review latency are independently auditable; missing class separation is No-Go. | No silent deletion or promotion of map state from an unverified classifier. |
| Aging, forgetting, and bounded storage | Under a declared retention policy, 7-day runs stay within the configured byte quota, have 0 orphaned chunks, preserve a recoverable lineage, and show byte-for-byte replay after compaction/restart. | Go only if aging/forgetting decisions are reversible or provenance-preserving; quota breach or unrecoverable forgetting is No-Go. | No claim of indefinite memory, lossless retention beyond the declared quota, or deletion without policy evidence. |
| Rollback and multi-session relocalization | 100% recovery in injected bad-update, process-crash, storage-fault, and sensor-gap cases; report p50/p95 relocalization and rollback time with 0 partial publications. | Go only with a known-good checkpoint and deterministic recovery receipt; one leaked bad state is No-Go. | No safety certification or guarantee of recovery from arbitrary physical damage. |
| Fleet federation, privacy, and safe merge | In intermittent-link multi-robot trials, 100% of accepted updates retain robot/session identity, 0 cross-branch ID collisions or policy violations occur, and repeated merges converge to identical bytes; report bandwidth and storage bounds. | Go only with explicit authorization, redaction, key/identity, and conflict receipts; any raw-data policy violation or lost conflict is No-Go. | No mandatory cloud, unrestricted raw-data upload, or identity/privacy guarantee without an independently reviewed threat model. |
| Long-duration operation | Complete one 24-hour and one 7-day run per declared hardware/profile, including restart and crash injection, with availability, drift-vs-age, storage growth, and recovery distributions. | Go only when every interval and failure is accounted for; timeout, missing intervals, or a substituted shorter run is No-Go. | No extrapolation from short runs to 24-hour/7-day reliability. |
These targets remain future-only even when an experimental implementation is available. They are separate from the current competitive SOTA claim gate and cannot satisfy, replace, or relax its accuracy, runtime, memory, map-quality, fresh-holdout, or legal-provenance requirements.
Track B — application differentiation¶
Track B explores opt-in application layers around stable SLAM contracts. Each application must prove user value independently; a plugin seam or service interface is not evidence that the underlying SLAM algorithm is superior.
| Application | User value | Differentiator / technical capability | Plugin seam | Validation dataset or scenario | KPI | Go/No-Go | Dependencies | Timing | Non-goals |
|---|---|---|---|---|---|---|---|---|---|
| Warehouse/factory change intelligence | Detect layout, inventory, and safety-relevant changes between routine passes. | Separate operational change from seasonal motion and maintenance noise. | Versioned change classifier and map-event policy. | Repeated aisle/plant routes with staged pallet, rack, and access changes. | Change precision/recall, alert latency, false structural-change rate. | Go only on held-out interventions; otherwise No-Go. | Track A change aging; session provenance; review UI. | Near term | No autonomous workcell command, safety certification, or default-map mutation. |
| Mining and construction equipment | Keep localization and site maps useful while terrain, stockpiles, and equipment move. | Robust change classes and branch/rollback for large, partially observed sites. | Site adapter, submap policy, event exporter. | Multi-week quarry/site protocol with staged earthworks, occlusion, and GNSS degradation. | Availability, drift, change F1, recovery time, storage growth. | No-Go without degraded-sensor and restart evidence. | Long-term map lifecycle; calibration; rugged storage. | Mid term | No machine-control or progress-certification claim. |
| Agriculture and forestry | Support repeatable navigation as crops, foliage, and paths evolve. | Distinguish seasonal biological change from traversable structure. | Vegetation/change policy and domain dataset adapter. | Repeated row/forest routes across growth/weather phases with held-out paths. | Relocalization availability, drift by season, false structural-change rate. | Go only with seasonal holdout and privacy review. | Change classifier; multisession schema; sensor calibration. | Mid term | No agronomic diagnosis or yield claim. |
| Underground and GNSS-denied robotics | Recover and maintain maps where global positioning is absent or intermittent. | Local branch/recovery semantics and bounded offline event logs. | Offline recovery and map-store adapter. | Tunnel/mine protocol with blocked routes, sensor interruption, and restart injection. | Availability, time-to-relocalize, recovery success, bytes transferred. | Any unrecoverable branch or unbounded log is No-Go. | Crash-safe storage; relocalization API; fault injector. | Mid term | No mine-safety certification or guaranteed coverage. |
| Autoware localization/map maintenance | Keep road maps and localization assets synchronized with verified roadway changes. | Provenance-aware promotion and deterministic rollback. | Localization/map adapter, migration, approval-policy APIs. | Repeated urban routes with controlled lane, curb, and construction changes. | Availability, drift, rollback time, rejected-change rate. | Go only with approved-map audit trail; no production claim otherwise. | Stable versioned APIs; map migrations; Autoware adapter. | Mid term | No driving policy, planning, or production Autoware claim. |
| Construction progress and digital twin | Track geometry and site-state evolution for planners and owners. | Link spatial changes to time/session provenance and reviewable versions. | Event log and digital-twin schema adapter. | Multi-week staged site scans with occlusion and planned demolition/build phases. | Registration consistency, phase-change F1, review latency, storage growth. | No-Go without independent phase labels and crash-safe replay. | Map schema; export API; compaction. | Mid term | No contractual progress certification or BIM replacement. |
| Urban infrastructure inspection | Compare bridges, tunnels, utilities, or roads over repeated inspections. | Distinguish structural change from viewpoint, weather, and seasonal effects. | Inspection feature/change policy and evidence exporter. | Repeat inspections with controlled defects and weather/lighting variation. | Defect recall/false alarm rate, revisit alignment, recovery time. | Go only for evidence-assisted review; engineering sign-off remains out of scope. | Inspection dataset/protocol; provenance; reviewer tooling. | Mid term | No engineering sign-off or unverified defect claim. |
| Fleet/cloud-edge map federation | Share useful updates while robots remain productive offline. | Conflict-aware merge, bounded synchronization, and edge-first privacy. | Federation transport, resolver, and policy interfaces. | Several robots with intermittent links, divergent branches, and delayed merges. | Convergence time, cross-robot consistency, bandwidth/storage bound, conflict loss. | Any raw-data policy violation or lost conflict is No-Go. | Identity/key policy; transport; conflict resolver. | Long term | No mandatory cloud dependency or fleet-scale claim. |
| Disaster and low-connectivity response | Maintain localization/map continuity when infrastructure is damaged or disconnected. | Crash-safe local branches with explicit recovery and later reconciliation. | Offline event-log, recovery, and privacy/security plugins. | Intermittent-network, degraded-sensor, blocked-route, and restart scenarios. | Availability, time-to-relocalize, recovery success, reconnect bytes. | Go only as a non-safety-critical aid with recovery evidence. | Offline storage; relocalization; secure reconciliation. | Long term | No emergency-response certification or safety-critical authorization. |
| General autonomous-driving/robotics integrations | Give downstream systems a reviewable map lifecycle without forking core SLAM. | Stable adapters, policy plugins, observability, and migration contracts. | Versioned SDK and dataset adapters. | Public robotics/vehicle sequences plus a reproducible multi-session protocol. | API compatibility, replay determinism, availability, drift, storage bound. | No-Go until at least one concrete domain gate above passes. | Versioned APIs, schema migrations, benchmark kit. | Long term | No universal autonomy or SOTA claim. |
Shared OSS differentiation and promotion stages¶
Future OSS differentiation should be expressed through stable versioned APIs, map schemas and migrations, an append-only event log, policy plugins, observability hooks, dataset adapters, and reproducible benchmark kits. The promotion sequence is deliberately conservative:
- research spike;
- experimental plugins;
- shadow mode;
- opt-in production;
- default candidate review.
Each promotion preserves the existing deterministic replay, accuracy, runtime, memory, map-quality, and Autoware bundle gates. Until the Track A and Track B evidence is independently accepted, all of this remains future-only planning: no implementation, default behavior change, README/SOTA claim, or production authorization follows from this document.
Status: proposed, 2026-08-24. This roadmap changes architecture and contributor experience, not the default SLAM algorithm. Every migration step must preserve the existing deterministic replay, accuracy, runtime, memory, map-quality, and Autoware bundle gates.
Additive competitive execution-selection handoff (2026-08-26)¶
The r3 execution-selection work is a future promotion handoff only. Its
candidate is NOT_READY and non-promoting while legal/source, dataset/fresh
holdout, machine, image, and toolchain closures remain external. It does not
alter the active profile, historical receipts, or any Track A/Track B claim.
Any future promotion requires an independently reviewed READY receipt and a
canonical profile reseal; unsigned handoff artifacts are review inputs only.
The additive r3 external-capture utility now provides a bounded observation
boundary for the three pinned image identities and an explicit opt-in,
network-none/read-only toolchain probe. Its immutable outputs remain
NOT_REVIEWED_EXTERNAL and non-promoting, including when all synthetic probes
pass; missing local images and probe failures remain PENDING. Custodian review
and independent execution receipts are still required before any promotion.
0. Current status matrix (2026-08-23)¶
Current-source registration matrix audit (2026-08-24)¶
The earlier Humble/Jazzy external-consumer and author-template entries below
are historical claims, not current-source promotion. The additive
registration-plugin-matrix-current-2026-08 profile and
scripts/audit_registration_plugin_matrix.py now require a content-hashed
manifest covering the current transaction/provenance/ODR changes. The local
result is Jazzy PASS_HOST_TOOLCHAIN_ONLY; the host has no /opt/ros/humble,
so Humble is NO_GO here. Historical claims without immutable receipt
sidecars, and the missing /tmp ODR artifacts, are explicitly superseded.
No build, container, replay, GT, scorer, map, or SOTA claim follows from this
audit; a clean C++14 external build/load receipt remains a distro-specific
gate. The new current-source release matrix is stricter and has four required
legs (Humble/Jazzy × optional-dependency absent/present). Its present legs are
now READY_PINNED_STATIC_REVIEW: official immutable fast_gicp and small_gicp
archive, source-tree, and license pins are checked in, but no distro build/load
PASS is claimed. Archive verification precedes extraction and post-fetch
network-capable commands are forbidden. The release status remains pending
until all four immutable runtime receipts are PASS.
| Area | Status | Evidence and next gate |
|---|---|---|
C++14 RegistrationPlugin API, typed request/result, capabilities, and failure contract |
Implemented | registration-plugin-api.md; installed interface and contract tests pass. The public C++14 interface is consumed by the clean external fixture below. |
Shell pluginlib loader and host/pluginlib hybrid resolver |
Implemented / experimental | Offline and explicit live-startup discovery, provenance, API/capability/config validation, and failure tests pass. Actual DSO load/session, constructor rejection, activation rejection with lease preservation, post-commit rollback, scanmatcher resource rollback, and backend preflight rejection now pass targeted gates. Broader live-bag characterization remains pending. |
| External author SDK/template and contract guide | Implemented / distro matrix passed | registration-plugin-authoring.md and the C++14 template build as clean external consumers on Humble and Jazzy. The installed interface now exports its contract helper, removing the source-tree reach-back. CI adoption and additional third-party author feedback remain next. |
| Built-in NDT same-translation-unit adapter path | Implemented | The numerical/configuration default remains unchanged, but startup and processing now use the host-resident session and activation transaction even when the explicit plugin selector is disabled. The HILTI exp04 baseline/adapter frontend and map-artifact receipts are byte-identical (the existing indoor absolute profile still has its documented violation). |
| GICP and optional Small GICP/VGICP adapters | Experimental / Humble+Jazzy build verified | Real optional-dependency builds and all direct-fixture adapter tests pass on Humble and Jazzy. These are compatibility results, not absolute-accuracy claims; real-bag characterization remains pending. |
| FAST_GICP / FAST_VGICP | Implemented conditionally / Humble+Jazzy build verified | The optional fast_gicp-only DSO provides typed FastGicp/FastVGicp adapters, plugin XML, variant-specific host factories, canonical preflight mapping, and fail-closed absence behavior. Dependency-enabled compilation and direct-fixture equality pass on both supported distros; real-bag accuracy/runtime characterization remains pending. |
| Backend loop-registration/plugin seams | Implemented / experimental | Live and offline graph_based_slam NDT now resolve the same host-resident lidarslam_builtin/NdtOmp backend_loop request/session before observable processing; BackendCore consumes only the typed interface. R2, the path-independent R4 provenance fixture, the M4a receipt/parser fixture, and the pinned MID-360 three-run artifact comparison pass. That historical receipt fails only the strict max-RTF gate (1.006913460 > 1.0); the M4b bounded-cache implementation/tests and formal stride-5 MID-360 development-profile gate pass (max RTF=0.264233831, wall CV 2.484173052%, peak RSS 565.222656250 MiB). M4c HILTI exp04 and exp07 three-run backend regression gates also pass with old optimized artifacts exact; the paired exp04 map check passes at 2%, while the unchanged indoor absolute profile fails on both old/current reports. This closes cache/general regression for these receipts only; official dense-GT/SOTA comparison and broader promotion remain M5 pending. GICP stays an explicit legacy bridge. |
| Competitive SOTA evidence validator | Implemented / fail-closed; identity frozen, evidence pending | Additive schema-v2 mode in evaluate_competitive_suite_gate.py separates historical exp02/03/21 regression slots from the primary-fresh partition. Every system must provide every dataset in both partitions with exactly three run records; completion, RTF/RSS, map, and per-sequence regression checks cover both, while aggregate APE and hierarchical CI use fresh only. It requires profile-assigned fresh slots (selection/input/reference/calibration hashes), all rivals, pinned per-system provenance, a common scorer fingerprint, an equal canonical seven-field thread policy, and the remaining safety evidence. Exp14/16/18 are now frozen_unopened after a read-only deep verification of the managed root; the execution-identity preflight is PASS before first run, while benchmark evidence remains INCOMPLETE until all required 3-system x 3-slot x 3-run records exist. Exposed exp02/03/21 assets cannot be relabelled fresh. Synthetic boundary/negative tests pass and no README claim is authorized. |
| M6a GT-blind execution harness | Implemented / campaign4 GT-blind complete; RTF gate not passed | scripts/run_competitive_gt_blind_benchmark.py emits an explicit 27-attempt plan and supports read-only dry-run/preflight plus a separately gated execute mode. M6a2 is retained as an immutable infrastructure-failed parent (a5abafde…f002), not replaced; campaign3 remains an incomplete lineage record. M6a3 repairs the ours RKO runtime contract, attempt-scoped GLIM ROS state, and FAST loopback-only ROS1 startup. The rebuilt ours image is sha256:18198c…288bd; synthetic full-path smoke passes for all three wrappers. M6a7 fixes and audits the comparable aggregate process-tree RSS contract (memory.max=max, OOM delta zero). Campaign4 completed all 27 GT-blind attempts with valid integrity evidence, but the finite processing-RTF gate is not passed (max RTF=1.173445611 from FAST-LIVO2, threshold <=1.0). No accuracy, map-quality, performance-superiority, SOTA claim, or M6b authorization follows. |
| M6a10 online-compute phase contract | v1 immutable / fixed10-v2 FAIL_CLOSED / fixed10-v3/v4/v5 preflight FAIL_CLOSED / fixed10-v6 runner-not-started FAIL_CLOSED / fixed10-v7 identity FAIL_CLOSED / fixed10-v8 identity FAIL_CLOSED / fixed10-v9 quiescence FAIL_CLOSED / fixed10-v10 completed GT-blind functional / FAST v2c retry-v2 runner-contract FAIL_CLOSED / FAST v2c retry-v3 quiescence FAIL_CLOSED | Fixed10-v2 retains exact consumer PASS evidence but is invalid overall: exit 125 for missing input_end, callback max 0.371609411 > 0.25 s, and RSS jitter 139.5325092% > 100%; all hashes are pinned, retry 0, GT/scorer false. Fixed10-v3, v4, and v5 each had one immutable read-only quiescence preflight failure and therefore never started a runner: v3 exceeded CPU/load with eight compilers, v4 exceeded CPU/load with seven compilers, and v5 recorded CPU busy 25.717884130982366% > 5% with one compiler (load1/CPU 0.2575 <= 0.5). Fixed10-v6 had a PASS quiescence receipt (CPU busy 3.0264817150063053%, load1/CPU 0.03875, no forbidden processes), but its preflight-to-run continuity window was lost before runner start; it is retained as immutable runner_not_started_after_preflight FAIL_CLOSED with retry 0. Fixed10-v7 ran once and failed closed before quiescence because its launcher tree hash bcbb4c86…f1b8ee6eb did not match the preregistered profile hash; independent canonical recomputation matches the corrected 64-hex tree identity. Fixed10-v8 ran once and failed closed before quiescence because the adapter bound a malformed 69-character image digest while Docker inspection returned the full pinned 71-character image ID; marker/closure hashes are recorded in the profile, runner start was not attempted, and retry remains 0. Fixed10-v9 passed its read-only identity receipt but its one owned quiescence attempt failed closed at load1/CPU 0.72375 > 0.5 (CPU busy 2.4439405391786346%, no forbidden process); no runner started and retry remains 0. Fixed10-v10 then ran exactly once from a single launcher process with successful quiescence continuity. It completed the GT-blind functional unpaced-ack contract with 230895 expected/received/processed messages, zero drops/overflow/failures, EOF and empty drain, callback maximum 0.109221778 s, and online compute RTF 0.126860008821509; aggregate process-tree RSS was 868278272 bytes, cgroup total peak 2750537728 bytes, memory.max=max, OOM delta zero, and the trajectory hash was byte-equivalent for raw/corrected output. The closure receipt SHA is e7224eb29dc547a5119cb7ecc1d51d009ced43cfca8e23c6c6d2fc1977e2e82b; no map artifacts, GT content access, scorer, retry, accuracy claim, performance comparison, SOTA claim, or M6b authorization follows. FAST-LIVO2 v2c retry-v2 was then quiescent (busy 3.7616763443574857%, load1/CPU 0.415) but failed closed in the host runner before Docker because safety.ground_truth_mount_exposed was absent; closure receipt c76ce0a5…ffdf0, no bag replay/container, retry 0. FAST-LIVO2 v2c retry-v3 then failed its single quiescence attempt before runner start (busy 98.275%, load1/CPU 0.69375, seven C++ compilers and one rustc); closure receipt eed5aa50…bc399, retry 0. |
| The fixed10-v2 image-bound launch overlay is separately content-addressed at | ||
d45545717f90f6877b5f281fc5623df04b824f7a236d2fe73b91c2dd3714371c and is |
||
| installed/label-checked in image | ||
m6a10-v2a-fixed10-v2-lidarslam-ours:jazzy (sha256:385b6eeedae3014bcd893849f2ec3a49f5176f0ef3cdd7e96559690e8dc25a69). |
||
| The preregistration remains unexecuted; the fixed9/v1 failure lineage is | ||
| unchanged. |
GLIM fixed10-v4 is a separate completed functional GT-blind closure, not a
rewrite of the ours fixed10-v10 or the GLIM fixed10-v3 failure lineage. Its
common phase finalizer receives the preregistered backlog bound 100000; the
single run passed exact consumer counts, EOF/drain, callback bound, RSS/OOM,
and input-only mount checks. Closure receipt SHA is
f33953c4126939dfce841b030cdb2e5560615d42963ae4aaaf4ab0aad7a7f6c1.
Online RTF remains diagnostic and no accuracy, SOTA, or M6b claim is
authorized.
Fixed10-v10 is recorded as completed functional GT-blind validation under
m6a10-v2a-ours-rko-unpaced-ack-fixed10-v10. Its independent launcher is
scripts/run_m6a10_fixed10_v10.py (SHA-256
f1d7844eaf8f2d5c22431fed5abcc84ae07f9f34cbd92de3b515c61d20ce78ed) and its
test SHA is
b89afc720fec4cc0af2d0736367407cbb6dc4b8789a26e3a0a3adf56530b2283. The
launcher pins v9 source SHA
879fa1c44aa093b158e405806276935bc41bf3bf4d9134a4534cd4922312254f, the
complete image ID
sha256:385b6eeedae3014bcd893849f2ec3a49f5176f0ef3cdd7e96559690e8dc25a69,
and canonical input tree
0a45497ab4ed94bf8e9757bab3f37e5786fee4991beea16c1efdc49e38cb926.
The read-only identity receipt is
/media/user/aiueo1/benchmarks/m6a10_training_20260822/ours_m6a10_v2a_unpaced_ack_fixed10_v10_identity_preflight/identity_receipt.json
with SHA-256 2211744e3691df3fc71f7b0a81248a2e75daa4c5b15a23c8a0f8acab4b360ab2;
its read-only identity receipt was followed by exactly one launcher-owned
quiescence and Docker run. The execution root and closure receipt are
content-addressed in the profile; runner return code, consumer/phase status,
RSS/OOM, no-map, and GT-blind completion checks all passed. This remains
functional validation only, not a performance comparison or SOTA claim, and
M6b remains unauthorized.
M6a10-v2b GLIM consumer hook (2026-08-23)¶
GLIM's benchmark-only consumer hook image build and installed-image identity
verification pass. One fixed10-v2 functional replay was executed once, but
the overall closure is INVALID_SAFETY and is not promoted.
Pinned core and ROS2 source revisions (faa264a1… and 4a9e7a4c…) receive
content-addressed patches that record callback acceptance, exact NTU topic
counts (LiDAR 5793, IMU 225102, image 5792), EOF before wait(), and final
queue drain before save. Queue observability and high-water bounds are
required; unknown drops or overflow are invalid rather than assumed zero.
The runner binds the preregistered input/config tree hashes and image labels,
including BUILD_WITH_CV_BRIDGE=ON; its host wall RTF remains diagnostic.
The verified image is
m6a10-v2b-20260823-glim-cpu-benchmark:competitive-v1 with ID
sha256:010c0019a077116edf4d1e7462dfa28561c4fb17db3b5db52e3652c8a875eb41.
The read-only build receipt is
/media/user/aiueo1/benchmarks/competitive_build_evidence/m6a10-v2b-glim-closure-20260823/build_receipt.json
(SHA-256
fe4d7e3b3b3fec28185f5faa016b19dfb30bd9a52928b099a68d6679e0a09df2).
The system-level execution identity was independently preflighted in a
read-only, network-none container. The PASS receipt is
/media/user/aiueo1/benchmarks/competitive_build_evidence/m6a10-v2b-execution-preflight-20260823-v2/execution_identity_preflight.json
with SHA-256
0ae210d6df457fb6bebf7c23ee33cb4e9299bcfe5f0ddde2ad009f79b1dd5179.
It binds the current runner/wrapper and recipe hashes, image ID and OCI
labels, installed callback markers, and system-container toolchain probe;
no bag, GT, scorer, or replay was opened. The earlier generator receipt is
retained as superseded because it used a non-contract config tree hash; the
v2 receipt uses relative_path_size_content_sha256_v1.
The synchronized profile canonical SHA is
800f07184b728623710375c778624a4f623bc706a1f8c05b19b544847d6e3830; its
execution-selection file binding is
9038c02be377a9ac9cc6fc15a34e9f23fc0181b57f31a5053d8f7c1a4aa00db2.
The immutable closure receipt is
/media/user/aiueo1/benchmarks/competitive_build_evidence/m6a10-v2b-execution-closure-20260823/closure_receipt.json
(SHA-256
72d8ef9cd2c8e26d4c2120463fdbff3057a75e3f81203863049c11028baa41d5), with
output tree SHA-256
f0db95c9c700b8c9650e9ce2751eb716c2cc271c664ee971f337ec6c01da2552.
The wrapper/phase sub-result passed exact expected/received/processed
236687, zero drops/overflow/failures, EOF and empty drain, callback maximum
0.0149106 seconds, process-tree RSS 1334648832 bytes,
memory.max=max, and OOM delta zero; online acknowledgement RTF
0.29150756632664043 is diagnostic only. The first direct launch mounted the
release parent directory, making sibling ntuviral_gt reachable even though
no GT file was opened. Strict GT-unreachable closure is therefore false; a
future attempt must mount only the canonical input directory and use a fresh
root. Retry is zero. The atomic consumer evidence and EOF sidecar remain
immutable; no scoring, accuracy, performance comparison, SOTA claim, or M6b
authorization follows.
The fixed10-v3 successor narrowed the Docker input bind to the canonical ROS2
directory only (no release-parent or ntuviral_gt sibling mount). Its identity
preflight is
/media/user/aiueo1/benchmarks/competitive_build_evidence/m6a10-v2b-execution-preflight-20260823-v3/execution_identity_preflight.json
(SHA-256
9b8cc1d089a73113930557c6558a32a5ded3725d386b20a78c61fc14e740e190). The
single replay retained exact consumer PASS evidence (236687 messages,
zero drop/overflow/failure, EOF, final queue 0, callback maximum
0.025782 seconds), RSS/cgroup evidence, memory.max=max, and OOM delta zero.
It remains INVALID_SAFETY, however, because the generic phase finalizer used
its default backlog bound 0 instead of the preregistered 100000, rejecting
the observed high-water 249. The immutable v3 closure is
/media/user/aiueo1/benchmarks/competitive_build_evidence/m6a10-v2b-execution-closure-20260823-v3/closure_receipt.json
(SHA-256
492d39b1f7c4a1a2e86893dd979b0ed57b3f37267cb7f21849b990d95bcc32ff). The
output tree is 3ab59eed2f20c3bf179bbfa05d0b5a4ffed723137ea81ebc2427619fd26306c5.
No retry, GT/scorer access, performance comparison, or M6b authorization
follows. The synchronized profile canonical SHA is
d83a3b96ea224f3f4673dd48a6252af488834dc272f735c5d5e147ce6a7a0ec4; the
execution-selection file SHA is
6ddcc24b5d221ef96ac746a7f0b4c6c3e261f5c002d115d51d19eb5fa8e072f4.
FAST-LIVO2 fixed10-v4 is likewise retained as an immutable FAIL_CLOSED
diagnostic, not a performance result. Its host stop/force-kill sequence had
no OOM evidence, and first-ACK absence is not proven because v4 had no
progress checkpoint. The preregistered v5 observability slice adds atomic,
line-buffered feeder progress, stable container name/cidfile, and
non-destructive reconnectable Docker lifecycle snapshots. It preserves the
algorithm, input, mount graph, and fairness contract, requires a rebuilt image
label, and has not started quiescence or replay. No FAST performance or M6b
claim follows.
| Live-node plugin preflight | Implemented / experimental | Read-only registration_plugin_enable, registration_plugin_class, and registration_plugin_allow_external are validated before pub/sub creation. The default selector preserves built-in behavior through the same host-session processing boundary; runtime hot reload is rejected. External DSO promotion remains No-Go until independent ODR, lifecycle, rollback, and Humble/Jazzy replay gates pass. |
| README superiority claims | No-Go for new claims | Existing sequence-scoped comparisons remain; no universal or plugin-performance claim is authorized. |
The status table is normative for planning. A replay receipt may establish offline compatibility without authorizing live default promotion or an accuracy claim. The live preflight row authorizes only explicit startup selection and provenance/failure handling; it does not authorize an external DSO accuracy or production-performance claim.
M5a competitive evidence-validator slice (2026-08-21)¶
The first M5 implementation is deliberately a validator, not a benchmark
result. The additive schema-v2 mode of
scripts/evaluate_competitive_suite_gate.py preserves the old report-only
--gate path while rejecting incomplete victory evidence. It does not treat
the exposed/frozen exp02, exp03, or exp21 slots as fresh. Instead, the profile
has a separate fresh_holdout_slots contract. M5b preregistered Exp14, Exp16,
and Exp18 in configs/slam_benchmark_profiles/fresh_holdout_selection_2026-08.yaml;
M5d now records their deep-verified identities with status frozen_unopened.
The execution preflight is ready/PASS, but any real v2 evidence invocation
remains INCOMPLETE until every system/run repeats those identities. The
selection contains no performance data and does not authorize a README or
SOTA claim.
The contract fixes two explicit partitions: the exposed/frozen historical
holdout_slots (exp02, exp03, exp21) as a regression partition, and
profile-assigned fresh_holdout_slots as the primary-fresh partition. ours
plus every required rival must provide exactly three records for every dataset
in both partitions, including failed records. Historical records are required
for regression checks but are excluded from aggregate APE and CI; fresh records
alone determine the >=10% aggregate and superiority interval. Pinned
revision/container/toolchain/config provenance remains system-specific, while
the scorer fingerprint and input/reference/calibration/machine/hardware/
thread/Release identity are common. The required thread policy is
cpu_affinity, max_threads, omp_num_threads, openblas_num_threads,
mkl_num_threads, tbb_num_threads, and accelerator_policy, with a
canonical mapping hash recorded in the receipt. Complete runs must provide
finite APE, processing RTF <=1, peak RSS, map metrics, artifact hashes, and
zero catastrophic/verified-false-loop evidence.
Accuracy is an aggregate over datasets, not nine pseudo-independent runs. The
three runs are averaged within each dataset; the fixed seed 20260821
resamples dataset clusters for 10,000 draws and requires the 95% percentile
lower bound of the best-rival-minus-ours APE difference to be positive. A
minimum aggregate improvement of 10%, per-sequence primary regression at most
2% against that sequence's best rival, per-dataset/per-rival map non-regression,
RSS, fresh-slot completeness, and all-rival completeness are separate required
checks. The 95% CI is a fixed-seed two-stage bootstrap: resample fresh dataset
clusters, then independently resample the three runs within each selected
dataset for ours and each rival; runs are never treated as pseudo-independent
datasets. The validator emits matching JSON/YAML PASS, FAIL, INCOMPLETE,
or INVALID receipts and records profile/evidence SHA-256 identities.
Synthetic pass, exact-boundary, false-freshness, pending-slot, hash-mismatch,
failed-run, old-schema, identity, RTF, per-sequence/map, and all-rival CI
negative tests are the current evidence; real fresh-slot competitor evidence
has no run records yet and cannot support a README or SOTA claim.
M5b fresh-holdout selection and identity freeze (2026-08-21)¶
The read-only exposure audit selected three HILTI 2022 additional sequences
with official dense 6DoF IMU references and the same Phasma sensor/calibration
contract: Exp14 Basement 2, Exp16 Attic to Upper Gallery 2, and Exp18 Corridor
Lower Gallery 2. The machine-readable selection receipt is
configs/slam_benchmark_profiles/fresh_holdout_selection_2026-08.yaml; its
profile hash is repeated in all three fresh slots. The receipt pins official
revision e62017f907007fdc5ab8c721842e4ae7359d7f49, bag sizes and LFS hashes,
GT file blob identities, calibration YAML blob identities, license, exposure
audit commands, and the blind policy. It intentionally leaves downloaded
input-manifest, GT-content, and calibration-archive SHA-256 fields were null
at preregistration time. The external managed root is now deep-verified
read-only: all three slots are frozen_unopened, with opaque GT hashes,
canonical calibration-tree hashes, canonical ROS2 tree/semantic hashes,
input-manifest hashes, manifest file hashes, preparation-receipt file hashes,
and plan identity recorded in the selection and profile. The frozen marker and
manifests remain bound to the committed preregistration SHA; the enriched
selection receipt records that SHA as an explicit lineage anchor, and the
verifier accepts only the current SHA or that declared anchor. No GT content,
trajectory metric, benchmark run, or performance result was opened or
recorded. The execution identity receipt is ready and its read-only preflight
is PASS; the schema-v2 evidence gate remains INCOMPLETE until all required
3-system x 3-slot x 3-run records exist. No README/SOTA claim is authorized.
Freshness remains bounded to the recorded repository/workspace/media audit and
is not proof of external historical-use absence.
After a separate review has produced all three frozen_unopened slots, the
independent read-only verifier is:
python3 scripts/verify_competitive_frozen_holdouts.py \
--root /media/user/aiueo1/benchmarks/competitive_holdouts/fresh_20260821 \
--selection configs/slam_benchmark_profiles/fresh_holdout_selection_2026-08.yaml \
--output <out>/frozen_holdouts_deep_verification.json
It requires exactly exp14, exp16, and exp18; checks marker/selection/
plan binding, raw bag bytes/LFS SHA and calibration bytes/Git blobs, safe
canonical ROS 2 metadata/tree and seven-topic semantic identity,
input-manifest payload, and
preparation runtime/argv hashes. The preparation receipt's
manifest_sha256 is checked by rebuilding its deterministic pre-finalization
manifest view and hashing the canonical JSON file bytes, including its
trailing newline (the payload hash is distinct). Raw bag content is checked by expected byte count and the
selection's LFS SHA-256; its preregistered Git-blob OID is the pointer
provenance, not a content-blob comparison. Ground truth is hashed as an opaque
stream only: no GT path, text, or metric is printed. Manifest and
preparation-receipt file SHAs are included in the machine-readable summary.
It is independent of the freezer; this M5d checkpoint reran it against the
managed root without parsing GT. Its output records both the current enriched
selection SHA and the accepted preregistration lineage anchor.
M5b execution-identity freeze (2026-08-21)¶
The next pre-run artifact is
configs/slam_benchmark_profiles/competitive_execution_selection_2026-08.yaml.
It records the ours/GLIM/FAST-LIVO2 repository revisions and URLs, runner and
configuration hashes, container recipe/tag/digest state, toolchain source,
scorer fingerprint, machine-fingerprint source, exact Release, and the
seven-key thread policy (cpu_affinity, max_threads, OMP, OpenBLAS, MKL,
TBB, and accelerator policy). The receipt now records a verified clean ours
revision, fresh machine fingerprint, and CPUs 0--7 with all thread limits set
to 8. GLIM's pinned image is now verified at
sha256:3702b73873395880e1dcdb91394232f1a9932194de22214e1aacb9626ced5846
with toolchain fingerprint
805429f650c6d927497e3c06169f9763c2cfd8e8101f02e6fcd565c0972924a2; PCL is
explicitly not_applicable for its CPU path. The execution identity receipt
is now ready after the deep-verified fresh-input identity was recorded; this
is a pre-run readiness state, not benchmark evidence. Ours is recorded at image
sha256:4426d334ee7014d6387694df8957285a56123c31576b17e30de655248dd91930
with toolchain fingerprint
6cdff5854c86cc820d6781700d7613b2e529e75d58c9fb0830fd2b0f792adca5, and
FAST-LIVO2 is recorded ready at its pinned image/toolchain receipt. The
recorded policy must be enforced with
taskset/Docker --cpuset-cpus plus the explicit OMP/OpenBLAS/MKL/TBB
environment variables before execution. FAST's upstream visual configuration is
an observed external-container artifact at
/opt/fast_livo_ws/src/FAST-LIVO2/config/HILTI22.yaml, SHA-256
efae9e702c71c770b19002b6e19d4e1b6f46c67df3727e984981d932258f0b4a, bound to
the immutable image
sha256:ddc75b574f8cca1e111332153e31a65c74ccdb11f8059da3797ab130814ce17e.
The checker validates that container-path/digest binding without reading a host
path. GLIM's CPU track disables visual input while preserving the canonical
camera messages for the cross-system input contract.
scripts/check_competitive_execution_selection.py is a read-only,
fail-closed preflight. It verifies the profile's receipt path/SHA, file and
tree hashes, 40-hex revisions, sha256:<64hex> container digests, exact
Release, scorer/machine files, and complete equal thread policy. It emits
machine-readable JSON/YAML and reports missing values as INCOMPLETE; it does
not build, download, open GT, or run SLAM. The M5d identity checkpoint now
reports this preflight PASS after the deep-verified frozen fresh inputs were
recorded; the separate schema-v2 evidence gate remains INCOMPLETE until
benchmark run receipts exist.
The scorer digest is recomputed from sorted canonical JSON entries containing
file name, repository-relative path, measured SHA-256, and policy; a stored
label cannot override a changed scorer. The CLI records profile SHA, execution
receipt SHA, scorer fingerprint, and the canonical thread-policy hash. Pending
status values are not accepted merely because their digest fields are filled.
The profile/receipt hashes use canonical_profile_sha256_v1: the parsed full
profile mapping is deep-copied, only
competitive_slam_profile.evidence_gate_v2.execution_selection_receipt_sha256
is removed, and the result is encoded as sorted compact UTF-8 JSON before
SHA-256. The receipt stores this canonical profile hash and kind, while the
profile stores the raw full-file receipt SHA. No other profile mutation is
excluded, so wrong-kind or mismatched hashes fail closed without a mutual-hash
cycle.
M5d pre-run identity checkpoint (2026-08-22)¶
The managed root
/media/user/aiueo1/benchmarks/competitive_holdouts/fresh_20260821 was
deep-verified without parsing GT or running SLAM. The enriched selection
receipt is frozen_unopened; its current file SHA is
2bfc541a8d6127599f7a36e66c08da44488a08a55a4d9c4709703223be8bdd2b, and its
explicit lineage anchor is the committed preregistration SHA
cfc106a0396cc032f297f72f10e549ebef55c39f492afe9c1d25ded0d4307dde.
The verifier accepted only those two identities and passed all three Exp14,
Exp16, and Exp18 manifests, opaque GT hashes, calibration tree, canonical ROS2
tree/semantic report, input manifest, and preparation receipt identities.
The historical M5d profile canonical SHA was
5a8b81b7483ce9921fdfb4393ed006390fc5f0b2553b5e29976de96725a4da39; the
execution receipt raw file SHA at that checkpoint was
ce6d03a273ec12d6191f9d802d1ee24892292d3333375fe10500c15a32a4abaf and its
status was ready. check_competitive_execution_selection.py reported
PASS, including all three pinned systems, common machine/thread/scorer
identity, and exact Release. This is a pre-run readiness checkpoint only:
the schema-v2 evidence gate remains INCOMPLETE until every system supplies
three complete runs for every historical and fresh dataset. No GT metric,
performance result, README change, or SOTA claim is authorized.
The capture/finalize workflow is
scripts/capture_competitive_execution_identity.py. capture emits a
machine-readable observation artifact for the current receipt: git revision,
tracked/untracked/clean provenance, non-secret machine fingerprint, explicit
thread-environment values, and local Docker image IDs. With an explicitly
bound local image, it additionally runs bounded --pull=never --network none
--read-only compiler/linker/ROS/PCL/Eigen/OpenMP probes and binds the
toolchain fingerprint to the inspected image digest; source bindings provide
Git provenance only. finalize verifies receipt ownership and emits a decision, but never
writes or promotes the reviewed receipt. It also records that no fresh bag or
GT was opened. The M5d receipt is now ready because the fresh identity was
deep-verified; the evidence gate is still INCOMPLETE until benchmark run
records are reviewed. A future run must still enforce the pinned system
containers, machine, Release, and seven-key thread policy.
Both commands accept explicit repeatable --source SYSTEM=PATH and
--image SYSTEM=TAG bindings; absent bindings produce an exact read-only
compiler/linker/ROS/PCL/Eigen/OpenMP probe manifest rather than guessed
readiness. The finite-state verifier compares measured revision, clean
provenance, image digest, toolchain fields/fingerprint, machine identity, and
canonical thread policy. Thus a complete ready/frozen synthetic contract can
be PASS, but no pending production receipt can be promoted implicitly.
M6a0/M6a1 GT-blind execution harness (2026-08-22)¶
The first execution slice is intentionally a runner and safety checkpoint, not
a benchmark result. scripts/run_competitive_gt_blind_benchmark.py fixes the
27-attempt order as ours -> glim_cpu -> fast_livo2, then
fresh_1(exp14) -> fresh_2(exp16) -> fresh_3(exp18), with repetitions 1--3
inside each slot. The default is read-only planning; --preflight additionally
inspects the receipt's immutable image digests and hashes the frozen raw and
canonical inputs, while --execute is a separate explicit action.
Each attempt is started in an unowned .part directory and atomically renamed
to its final directory for both success and failure. The manifest records the
schedule index, canonical profile/receipt/selection identities, raw and
canonical/semantic input hashes, config/revision/container/toolchain identity,
CPU/thread environment, exact command and mounts, host timing/RSS, artifact
hashes, and a GT-blind proof. GT paths are used only for metadata reachability
guards; their contents are never opened or passed to a container. No scorer,
APE, or map-quality command is part of this harness.
The managed input root is
/media/user/aiueo1/benchmarks/competitive_holdouts/fresh_20260821 and the
intended results root is
/media/user/aiueo1/benchmarks/competitive_results/m6a_gt_blind_20260822.
The M6a0 plan has schedule SHA
bb3484c32615a1c6af8b2f1f2e823bd7f06e8131d546a6f91a104707eb9766ce and was
generated without launching a container. M6a1 rebuilt the ours image from the
fixed recipe at algorithm revision 866f733677e92ecb08d67126e463da99dd140d46.
The image ID/digest is
sha256:0680ae359deb2da45ff16ecf1c5d92c0510dc51d48bd06c0fcd93ce1d33ff3fb;
its labels verify the ndt_omp_ros2 gitlink, the RKO-LIO gitlink, and the
exact local RKO archive. The separate GT-blind harness/orchestrator revision
is 4701f0084d6b0fff475a62bec7eeb6d807561821; it is not the algorithm
revision. The receipt and profile hashes were resynchronized without changing
the canonical profile identity. Read-only dry-run and preflight passed all
27 scheduled attempts, with no results-root marker created at that checkpoint.
Preflight hashes each frozen slot once, even though the schedule repeats it
across systems and repetitions.
M6a2 GT-blind execution attempt (2026-08-22)¶
After the M6a1 checkpoint, the fixed schedule was executed exactly once in the
managed results root
/media/user/aiueo1/benchmarks/competitive_results/m6a_gt_blind_20260822.
The run used the committed 3bd10254972ebbcbdc3f904f13f9a79f60e37837, the
M6a1 preflight identity, CPU set 0--7, and the quiescence snapshot
ae07b16bb19227468fa5f61327a9a343a2f43c5f5ebe6ade17b712d5b134db54.
All 27 immutable attempt directories were created and no .part directory
remained. The GT-blind completion manifest is
a5abafdeb420619a1460737a3cf91862fa41ce7930c041324b1f38c00b16f002 and has
status INCOMPLETE: ours had 9 exit-1 startup failures because rko_lio was
not discoverable in the runtime, GLIM had 9 exit-250 failures while creating
its ROS log directory on the read-only container, and FAST-LIVO2 had 9 exit-20
failures because its ROS 1 master could not self-connect with the network
disabled. Every attempt records ground_truth_content_opened: false and
scorer_invoked: false; no trajectory or performance result is valid. These
runtime contracts must be repaired and independently smoke-tested before any
rerun. No GT, APE, map-quality, accuracy, or SOTA claim follows from M6a2.
M6a3 GT-blind remediation and preflight closure (2026-08-22)¶
M6a2 remains immutable and is explicitly recorded as an infrastructure-failed
parent, not a replacement campaign. Its completion manifest SHA is
a5abafdeb420619a1460737a3cf91862fa41ce7930c041324b1f38c00b16f002; the
checkpoint lineage is commit 972300263476a113bd32e78b69babdbce5753895.
The algorithm revision remains
866f733677e92ecb08d67126e463da99dd140d46.
M6a3 repairs and synthetic smoke evidence are:
- ours image
sha256:18198c17627459e96c574b1bf3093064c9c092f4fc2f89594b7e4b14705288bd, with the pinned RKO package/archive/runtime layout verified; - GLIM attempt-scoped ROS state and process-group clean shutdown;
- FAST-LIVO2 network-none, loopback-only ROS1 master/node/bag path;
- all three synthetic contract markers report
performance_run: falseandgt_mounted: false.
Normalized smoke evidence is stored outside the repository at
/media/user/aiueo1/benchmarks/competitive_build_evidence/m6a3_20260822/smoke/normalized.
The tree hashes for ours/GLIM/FAST are
12e56407c0750bf47ff3aae307c4a96e78e056f84121421de10ed6d9825216ec2,
2e74d9659c04007af20e4fb5999087f1a6cbaac4a4cf3df47f6eacc8562edbed, and
8293e1f1771f9b60a8d11a43998f538e6b965341bc3399b4aeda81c1a209ce17.
The disclosed rerun uses
/media/user/aiueo1/benchmarks/competitive_results/m6a_gt_blind_campaign2_20260822.
Its dry-run is 27/27 with plan SHA
465e856846eb4bcf15e125285a914d2defda72413b5016e2965b884cda2f8bd8; its
preflight is preflight_ready, 27/27, with plan SHA
7aeac10cdeb1c8356e282b53a62f4f3a05621f37942d325d58302a8f6464cd43.
The preflight records canonical profile SHA
5a8b81b7483ce9921fdfb4393ed006390fc5f0b2553b5e29976de96725a4da39 and
checks all frozen input identities, image digests, CPU/thread policy,
GT-unreachable mounts, and scorer-free commands. At this M6a3 checkpoint no
--execute had been run; the later M6a4 partial record below is the complete
execution status. No GT content, scorer, accuracy, performance, map-quality, or
SOTA claim is authorized.
M6a4 partial campaign closure (2026-08-22)¶
Campaign2 was executed once after the fixed preflight. Final attempts 001--018
are immutable and all passed their GT-blind completion checks (ours 001--009,
GLIM 010--018; exit 0, complete output, no GT/scorer access). FAST attempt 019
reached wrapper shutdown and emitted its top-level status artifacts, but driver
finalization failed on the unreadable root-owned mode-600
attempt_019.part/ros_home/rospack_cache_15823137030970321179. The .part
directory was not changed, renamed, or deleted; no final attempt 019 manifest
was produced and attempts 020--027 were not started.
The atomic partial manifest is external:
/media/user/aiueo1/benchmarks/competitive_results/m6a_gt_blind_campaign2_20260822/partial_campaign_manifest.json,
SHA-256
df91a0f0852911790f3fabb5b5638938b8e1222208943be01678a99fbd062978.
Its root identity excludes the manifest itself and has projection SHA
ceafd30fbfe4f0099ba32e4a79390f8dd5cc0e6ea428254c8d021c11231bd16e.
This is an INCOMPLETE infrastructure record, not accuracy or performance
evidence. Host /usr/bin/time -v wrapped the Docker client, so its RSS values
are not valid container/cgroup peak-memory measurements. GT content and scorer
remain untouched. The immutable M6a2 parent SHA is
a5abafdeb420619a1460737a3cf91862fa41ce7930c041324b1f38c00b16f002; a
planned campaign3 must add resilient attempt finalization and cgroup RSS
accounting before any M6b evaluation or README/SOTA claim.
M6a5 measurement contract and campaign3 preflight (2026-08-22)¶
The M6a5 remediation makes container cgroup-v2 memory.peak the only
comparable RSS field (container_cgroup_peak_bytes). The host Docker-client
measurement (docker_client_peak_rss_kb) is diagnostic only. memory.max=max
is a valid unlimited-limit observation; missing, malformed, unreadable, or
non-atomic memory evidence fails closed. The helper, all three wrappers, and
the driver are bound in the execution receipt, together with the immutable
campaign1 completion and campaign2 partial-manifest lineage. The synthetic
known-allocation evidence is
e2df6b70dcec703406a406ed7a4c45aaac87b69987d87966f4d10bceee1c85bb and is
not a performance result.
Campaign3 uses
/media/user/aiueo1/benchmarks/competitive_results/m6a_gt_blind_campaign3_20260822.
Its external preflight evidence reports 27/27 readiness with no attempts,
GT content access, or scorer invocation. The final dry-run plan SHA is
bef6184b506b852288cf07b94772442cdc47c6c58e2cef45da5140249729d082, the
preflight plan SHA is
362ad49f85491bfd74ddda181ec0a334c972e4be2818374a05b0db3c5724087b, and
the summary SHA is
9ae7115d10dee09f5287317b5e9a3912580f7ae31cfd93e8686162149e108bf0.
The receipt/profile identities at this historical M6a5 checkpoint were raw
receipt
d89d30d9e516f7d7211536bd1ea4f837ae95c4f7aa4527af60a5f7699c3d677f and
canonical profile
5a8b81b7483ce9921fdfb4393ed006390fc5f0b2553b5e29976de96725a4da39.
M6a7 resynchronizes the current values in the section below.
This is an execution-readiness checkpoint only; M6b scoring and any
accuracy, performance, map-quality, or README/SOTA conclusion remain
unauthorized.
M6a6 campaign3 GT-blind closure (2026-08-22)¶
The fixed campaign3 schedule reached 27/27 immutable final attempts with no
remaining .part directory and no GT/scorer access. Ours and GLIM are 9/9
complete. FAST-LIVO2 is 6/9 complete; its fresh_2 attempts 022--024 are
preserved exit-22 failures (complete=false). Completion manifest SHA:
31df60ff2775d2f2a699e7559c16cc5d732a91e175f12af1db06bc47e4b8cd5b.
Integrity summary SHA:
af27ae2c7d019db790cace3b38b250c2a5dcd3340e58fa5a91158293b2bf2024.
The external closure manifest is
/media/user/aiueo1/benchmarks/competitive_build_evidence/m6a6_20260822/closure/m6a6-campaign3-closure.json
with SHA
44d50669e33dbb105300b2b3f926c99f891341373e6a1dfa71ae9c85acf954b8.
The campaign is INCOMPLETE, not a performance or accuracy result. Its
container_cgroup_peak_bytes is a cgroup total footprint including page
cache, not process RSS; multiple attempts reached the 4-GiB memory cap and
the observed cold/warm spread prevents separating reclaim/cache effects.
The Docker-client RSS is diagnostic only. memory.events was not captured
by the M6a5 schema, so pressure/reclaim behavior is explicitly unknown. RTF,
RSS, accuracy, map-quality, and SOTA gates therefore remain invalid or
insufficient. The disclosed lineage is campaign1 immutable failure,
campaign2 immutable partial, campaign3 incomplete, and campaign4 planned;
M6b remains blocked.
The reproducibility slice also owns the image recipes and their build entrypoint:
docker/ours_competitive_benchmark.Dockerfile,
docker/glim_cpu_benchmark.Dockerfile,
docker/fast_livo2_benchmark.Dockerfile, and
scripts/build_competitive_benchmark_images.sh. Each recipe pins an immutable
ROS base digest, source revisions, and the CPU-only thread environment. FAST
LIVO2 no longer relies on the historical undocumented
fast-livo2-benchmark:noetic/hdl_localization_noetic:local base; its pinned
full-length Sophus compatibility commit is recorded explicitly. GLIM's CPU
track explicitly marks unused PCL as
not_applicable; its container probe fingerprints that sentinel rather than
adding an unrelated dependency. The
ours recipe uses a Dockerfile-only context, clones the pinned public repository,
initializes only the build-required Thirdparty/ndt_omp_ros2 gitlink, and
checks HEAD/clean/submodule status before building. The pinned
Thirdparty/rko_lio object is supplied as an exact local archive whose SHA is
checked against the 622b74778a41f753d47aa5918043755ebcbd4c75 gitlink; no
public-mirror substitution is used. The static contract test binds every
recipe and entrypoint SHA to this receipt. FAST-LIVO2's pinned
image/toolchain, GLIM's pinned image/toolchain, and the rebuilt ours image now
have observed ready receipts. This proves reconstruction metadata and
GT-blind preflight only: the M5d
receipt is ready for a first run, but no benchmark, accuracy, performance, or
SOTA claim follows from this slice.
M6a7 process-tree RSS audit and campaign4 gate (2026-08-22)¶
The M6a7 final read-only audit is
/media/user/aiueo1/benchmarks/competitive_build_evidence/m6a7_20260822/v3_final_audit_tool_final_20260822/v3_run_audit.json
with SHA-256
bd7f57cd2cb6fe8b93a9c28d7b193968d3e865180f2985fb66c44c736e7cd818; its
normalized PASS receipt is the adjacent
m6a7_v3_final_receipt.json with SHA-256
06985d4c4900dbe1aa27687c023c4b6c2ddd3c362ed6782c92a048f5e1eae62e.
The corrected auditor handles leading whitespace in GNU time exit lines and
fail-closes missing, duplicate, or unexpected AB/BA directories. It verified
20 pairs/40 runs, all complete, with GT/scorer/campaign4 access false. Earlier
failed audit directories remain preserved as disclosed lineage.
Campaign4's only comparable memory metric is
aggregate_process_tree_peak_rss_bytes: the sum of per-process peak VmRSS
(shared pages may be recounted). The sampler is fixed at 250 ms/nice 10;
cgroup v2 memory.max=max is required and every OOM/oom_kill delta must be
zero. Cgroup total footprint and Docker-client RSS are diagnostics only.
The M6a7 measurement contract passed its fixed overhead gate (median absolute
1.8513%, bootstrap upper 4.1914%), signal/atomic-evidence checks, allocation vs
page-cache separation, and all three synthetic wrapper smokes. These are
measurement and isolation receipts only; they are not accuracy, performance,
or SOTA evidence.
The profile/receipt use canonical_profile_sha256_v1: only the registered
execution-receipt file SHA is excluded from the profile projection. The M6a7
audit/receipt/summary SHAs are explicit external bindings and do not depend on
mtime or a self-referential hash. At that M6a7 checkpoint, campaign4 was a
fresh-root, GT-blind/scorer-free dry-run and preflight with zero attempts; no
real replay was authorized in that checkpoint.
At that pre-run checkpoint, the campaign4 fresh results root was
/media/user/aiueo1/benchmarks/competitive_results/m6a_gt_blind_campaign4_20260822
and had zero attempts. Its final deterministic read-only dry-run plan SHA is
1ffa6836abc9aa94bb41e310de4ff2e50c9f336335b23779f0ccfa1236ea09f2 (a
repeat is byte-identical); the 27/27 preflight plan SHA is
a8c953e59b6a7dd70891fcdf3c57c791f9735ea6173dda114cd365e188562e4e.
Both bind selection 2bfc541a…dd2b, algorithm revision
866f733677e92ecb08d67126e463da99dd140d46, profile canonical
cbb09323…6cc25, execution receipt c58d1188…d14f7, and the M6a7
process-RSS audit receipt 06985d4c…ae62e; neither plan starts a container or
scorer. The subsequent GT-blind execution is recorded below; the plans and
fixed execution identity remain unchanged.
M6a8 campaign4 GT-blind completion (2026-08-22)¶
The fixed campaign4 root was executed only after the third quiescence window
passed (30744959f67ef3c6b9f67aa8e8663ba1412c2fc65ae65ef7e4b949dc81b214ca).
The pre-run machine snapshot is
07f97ffc4241be205a0678fa58bd5defc66fa6bc818d83716d901e3177ec2341.
Two earlier starts failed before any attempt because an already-created empty
root was rejected by the driver's no-overwrite contract; their immutable
evidence is 4bda0f3811e7bf3f9f4d9e38ea91b6f9c9be965e8272824c1862bb955fab9bd4
and 26873c3a35b5c70894ebccc24dd694c05060abc450195d7930423c9f6f9119fd4.
The verified empty root was then removed and the driver-owned mkdir recovery
was used; those starts created no containers and no attempts.
All 27 scheduled attempts (ours/GLIM/FAST-LIVO2 × exp14/16/18 × three
repetitions) exited zero and completed the GT-blind output contract. The
completion manifest is
f63b14f52c0b22b957f897568aa38f5a2fce26fea47bd2ca5917f6fec43c1c74, the
integrity manifest is
f28ba05ea5c8fce0a6944ddcc7353d3dd5ea38dca927a7dcef7e4131983b6e5b, and
the closure manifest is
f2b1b6e943f1c30cd54636c9221bd1eb38d18192cb1cce1f583fd17bf8b1c59a.
There are no .part files, Docker containers, or sampler residuals. Every
attempt has valid aggregate process-tree RSS evidence, memory.max=max, and
zero cgroup OOM/oom_kill deltas; Docker-client RSS and cgroup total memory
remain diagnostics, not the comparison metric.
The closure is deliberately
VALID_COMPLETE_GT_BLIND_RTF_GATE_NOT_PASSED: all finite processing RTFs were
recorded from bag metadata, but FAST-LIVO2 reached 1.173445611, above the
registered <=1.0 limit. No GT file was opened, no scorer was invoked, and
no accuracy, map-quality, aggregate APE, SOTA, or M6b claim is authorized.
The fixed profile canonical SHA remains
cbb093233b2740e0624fbd348ac293a705fd69e7fa1825723a4e7e493736cc25 and the
execution-receipt SHA remains
c58d11881f88dd7ea6fef05f1e91edf901ca5eaa23076ff673306580885d14f7.
M5c fresh-holdout acquisition checkpoint (2026-08-21)¶
The download implementation is scripts/freeze_competitive_fresh_holdouts.py.
It is intentionally a checkpoint workflow, not a benchmark runner. The exact
order is: independently review the selection receipt, run read-only plan,
run explicit download (or download --resume only for the same managed
identity), run verify, perform an independently reviewed ROS 2 conversion and
semantic-equivalence report, then run finalize. Only the last step can move a
slot to frozen_unopened; selection/execution receipts and the competitive
profile are not modified by the downloader and require a separate review.
The plan binds the official selection-receipt SHA, destination identity, and the runtime SHA-256 of the producer script. That producer hash is copied into the managed-root marker and every slot manifest, so a changed downloader or selection contract cannot silently resume an old staging tree. Final slots are fully re-verified and skipped; mixed final/staging, changed markers, stale partials, path traversal, symlinks, size/hash mismatches, and calibration tampering fail closed. Manifests and state transitions use canonical JSON and atomic replacement.
Raw HILTI bags are streamed and checked against their declared size and LFS
SHA-256. Ground-truth files remain opaque: the tool records only bytes and
SHA-256, never parses, prints, or scores their content. Calibration uses both
storage paths and canonical logical paths, Git blob IDs, and a tree hash. The
destination is the explicitly supplied
/media/user/aiueo1/benchmarks/competitive_holdouts/fresh_20260821 tree.
The external M5c acquisition/preparation was completed before the M5d review;
the independent deep verifier rechecked the final tree and all three slots are
now frozen_unopened. This records identity only: no GT content, trajectory
metric, benchmark run, or performance result was opened. The schema-v2
evidence gate remains INCOMPLETE until the required run receipts exist; no
README or SOTA claim is authorized.
The next conversion command is
scripts/prepare_competitive_fresh_ros_inputs.py --all (or
--sequence exp14). It requires exactly rosbags==0.11.0, rechecks only the
raw-bag bytes/SHA, and never opens the manifest GT path. The fixed
rosbags-convert --src BAG --dst STAGING --dst-storage sqlite3 --compress none
--src-typestore ros1_noetic --dst-typestore copy argv is recorded together
with Python/NumPy versions, converter/comparator hashes, canonical ROS 2 tree
hash, and the seven-topic semantic report hash. Conversion and comparison are
staged under canonical_ros2.part/semantic_equivalence.json.part, then
atomically published with preparation_receipt.json; --resume is accepted
only when that receipt, managed plan, raw identity, versions, commands, and
all output hashes still match. The final receipt is the commit marker; a
converter/comparator crash or either first publish rename can resume only when
each artifact has exactly one .part/final form; a staged receipt must have
full identity equality, while a converter/comparator partial without a
receipt must pass safe tree/report validation. The resulting canonical
tree/report are passed to the downloader's separate finalize step. The
recorded M5d receipt/profile update is a separate reviewed operation after
finalization; the freezer itself never edits those files.
M3 live backend NDT migration gate¶
The live graph_based_slam component now constructs the host-resident NDT
adapter in its own translation unit and resolves it through the shell-side
RegistrationResolver. The backend role uses an explicit typed request with
the historical loop defaults: resolution=ndt_resolution, epsilon 0.01,
maximum_iterations=100, step_size=0.1, outlier_ratio=0.55,
num_threads=ndt_num_threads, and DIRECT7. The resolver validates the API,
license, target policy, initial-guess/aligned-source capabilities, and mean
correspondence metric before initializePubSub(); failure is actionable and
there is no fallback or hot reload. Startup logs record backend kind, canonical
class, API/license, capability bits, and host provenance markers.
The live registration_method parameter is read-only as well, so a runtime
attempt to switch the backend is rejected rather than becoming a silent no-op.
The preflight barrier runs before sensor subscriptions and before the optional
degeneracy CSV is opened; constructor-owned TF objects exist earlier, but no
sensor data or map processing is observable before successful preflight.
BackendCore::searchLoopForSubmap() has a typed
RegistrationPlugin overload. It now submits the host-prepared source/target,
3D-BBS-derived initial guess, and consumes typed aligned cloud,
convergence/failure, transform, and fitness fields before applying the existing
loop gates and log lines. The offline runner is handled by the M3b section
below: its NDT path uses the same host-resident resolver/session boundary,
while its explicit GICP legacy bridge remains shell-owned. There is no PCL
overload or second registration implementation in BackendCore. Live GICP
keeps its legacy construction through the same typed bridge in this slice.
The R2 fixture runs both the direct legacy PCL object wrapped by
PclRegistrationAdapter and the host-resident NdtOmp typed adapter with the
same preprocessing/defaults; proposal pair/found state, relative transform,
fitness, convergence/failure path, overlap/gate result, and every log line are
bitwise-identical. Deterministic arrival batching, strict gate rejection, and
empty-cloud behavior remain covered. The preflight/bridge tests also cover
exact backend parameter mapping, invalid configuration rejection, empty target
rejection, and reset to kNotConfigured. The live component target
embeds the NDT implementation in the same translation unit and does not link
liblidarslam_default_plugins.so; only the loader shell DSO appears in
ldd. A full live/offline replay gate is intentionally not claimed here; it is
part of the remaining R4 real-bag gate.
M3b offline backend NDT migration gate¶
graph_slam_offline_runner now uses the same
backend_registration::BackendRegistrationRequest as the live component. The
request fixes role=backend_loop, class ID, API/license policy, required
capabilities, and sorted typed NDT parameters. The runner resolves
lidarslam_builtin/NdtOmp through RegistrationResolver, retains the session
and typed plugin for the full run, and injects only RegistrationPlugin& into
BackendCore. The concrete adapter is included in the runner translation unit
and the default plugin DSO is not linked, preserving the ODR-safe host path.
Before the first bag message, the runner writes
registration_plugin_receipt.yaml with backend kind, canonical metadata,
capabilities, requirements, typed parameters, and host/pluginlib provenance.
The shared path-independent canonical identity is covered by an R4 unit
fixture comparing two independent live/offline-style resolves. GICP remains
an explicit legacy PCL bridge, and unknown methods fail closed without NDT
fallback. No full real-bag R4 replay, external-DSO promotion, or README claim
is authorized by this slice.
M4a deterministic measurement and receipt gate¶
The reusable
run_offline_determinism_check.sh
harness now measures every backend run with /usr/bin/time -v, derives RTF from
the bag's declared duration, and emits per-run metrics plus YAML, JSON, and
Markdown summaries. It requires byte-identical loop edges, optimized
trajectory, and canonical registration_plugin_receipt.yaml across all runs;
the receipt is fail-closed for the backend_loop/host NDT identity and typed
NDT configuration. A versioned .complete marker stores the execution
identity (runner/script/setup/parameters/complete bag tree/optional input
hashes, overrides, and thread-affecting environment). Resume rejects changed
inputs or partial artifacts, while a non-empty output run is protected from
accidental overwrite.
The machine receipt includes git revision/dirty state, a tracked-diff and
untracked-content worktree fingerprint, compiler/CPU/memory/OpenMP settings,
and hashes for resolved dynamic dependencies. Optional RTF, peak-RSS, and
wall-time-CV limits are report-only when omitted and fail closed when supplied;
--require-ape similarly makes the existing reference-TUM scorer mandatory.
The fixture suite covers success, byte/receipt mismatch, malformed thresholds,
required-APE failure, marker/input invalidation, and stale-output protection.
This is an Implemented / experimental measurement tool. The formal pinned
MID-360 result below applies the existing R4/README evidence policy: it proves
three-run artifact determinism and reports a strict performance-gate failure,
not a new accuracy or SOTA claim. No default switch or new README claim is
authorized by M4a. Local fixture/documentation checks on 2026-08-21 passed
(15 harness tests, 6 docs-entrypoint tests, strict MkDocs, and diff-check).
M4a pinned MID-360 three-run receipt (2026-08-21)¶
The formal receipt used the frozen backend-input bag
/media/user/aiueo1/benchmarks/mid360_public/backend_input_20260713/backend_input
(341.250776531 s), the Jazzy install, and the checked-in
lidarslam_mid360_rko_graph.yaml (ndt_num_threads=0, DIRECT7; inherited
OpenMP environment empty/default). The complete local receipt is
/tmp/lidarslam-m4-r4-mid360-final.jSP22j. Its reproducibility identifiers
are recorded here so a future run can be rejected if its execution identity
changes:
execution_identity_sha256=f7bf00d1d3cb4ed91eaf5522dc6bdb48e169061b119c94677b4de9c86ae7e0dc
runner_sha256=04382d6e77a7faf362e9a21ea26be559a00ee082c728b536a974e5c5fa469103
script_sha256=7afae5d4e73c29fbd8a567938a69b2079784019e6bf2524db5281df957d85c1b
All three runs completed with byte-identical loop-edge, optimized-trajectory,
and registration_plugin_receipt.yaml artifacts. The measured rows were
wall_sec=343.16/339.08/343.61,
RTF=1.005594781/0.993638765/1.006913460, and
peak_rss_mib=548.10/548.26/547.95; wall-time CV was 0.595904306%.
With max_rtf=1.0, max_peak_rss_mib=672, and
max_wall_cv_percent=5.0, the receipt is FAIL only because the maximum RTF
was 1.006913460. RSS, CV, and determinism passed. This is a performance-gate
result, not a trajectory or registration-receipt compatibility regression.
The optional historical reference produced an APE-like proxy of
1.1072674022 m from 218 associated pairs (358 rejected). It is a
GLIM/reference trajectory proxy rather than ground truth, so it is not an
accuracy, SOTA, or competitor claim. README claims remain unchanged.
M4b bounded target-cache implementation slice (2026-08-21)¶
The first M4b code slice is implemented without changing the generic plugin
API or the frontend default. BackendCore::TargetCloudCacheValue now retains
only the representation required by the candidate variant: distance-only
candidates retain the final filtered target, ScanContext retains its filtered
target, and the 3D-BBS variant additionally retains its unfiltered BBS
aggregate. The variant is part of the exact key, so these representations
cannot cross-hit; bounded capacity, revision/pose invalidation, and the
existing 3D-BBS path remain fail-safe.
NdtOmpRegistration has an opt-in deterministic target-cell LRU. Capacity
zero is the default for frontend and external requests. The backend live and
offline canonical request sets the typed integer
target_cell_cache_capacity=3, which is emitted in the registration receipt.
Each entry retains the target shared pointer and a separately configured NDT
instance; a hit switches instances without copying target_cells_, while
prior, distance, exception, and reset guards remain per active instance.
Unit coverage includes representation variants, capacity/range, hit/miss,
deterministic eviction, reset, and backend receipt identity. This is an
implementation/compatibility result only; the MID-360 performance gate has
not been rerun and no speedup or RSS claim is authorized yet.
M4b performance-margin policy (fulfilled for pinned MID-360; holdouts pending)¶
The M4b acceptance policy targets 5% headroom (max RTF <= 0.95 across
three runs), while retaining the current hard gate of max RTF <= 1.0. Keep
the bag tree, params,
Jazzy/toolchain/dependency fingerprint, ndt_num_threads=0, OpenMP environment,
ROS domain isolation, and measurement script fixed. Profile first, change one
performance mechanism at a time, and record fresh runner/script/execution
identity hashes. Reject a candidate if required trajectory, loop-edge, or
registration-receipt hashes change, wall CV exceeds 5%, or peak RSS exceeds the
agreed non-regression budget. The formal pinned MID-360 receipt below meets
these performance/determinism gates. The same policy remains required for
HILTI exp04/exp07, GT holdout, and any broader production or accuracy claim;
no README/SOTA claim is implied by this profile-scoped result.
M4b stride-5 MID-360 development-profile candidate (2026-08-21)¶
Candidate 2 was evaluated with an explicit
loop_search_query_stride:=5 override on the pinned MID-360 backend-input bag,
then frozen in lidarslam/param/lidarslam_mid360_rko_graph.yaml as a
MID-360 development-profile setting. The run kept ndt_num_threads=0, unset
the OpenMP environment, and used the Jazzy runner with ROS domain isolation.
The complete receipt is
/tmp/lidarslam-m4b-mid360-stride5.JN9qW8; its execution identity is
54423801ae9bd9691f129f84f064dde6f5cb3e629356e334d0b280a3594839cf.
The single run measured wall 85.020 s, RTF 0.249142290, peak RSS
565.320 MiB, and CPU 495%. It accepted edge 6 -> 604 with fitness
0.47313574856385182. Loop edges, raw/optimized/refined trajectories, and
the refinement report were byte-identical to the stride-1 run; the
registration receipt was also identical. The historical GLIM/reference
trajectory proxy was unchanged at APE RMSE 1.1072674021694795 m from 218
pairs. This is a strong compatibility/performance candidate, but it is one
run only: three-run wall-CV/determinism, HILTI exp04/exp07 regression, and
ground-truth holdout gates are still required. The proxy is not GT, an
absolute-accuracy result, or an SOTA/competitor claim, and no README claim or
global parameter default is authorized by this receipt.
M4b formal pinned MID-360 three-run gate (2026-08-21)¶
The frozen MID-360 development profile was then executed three times with
the same backend-input bag
/media/user/aiueo1/benchmarks/mid360_public/backend_input_20260713/backend_input
(341.250776531 s), checked-in params, Jazzy runner, ndt_num_threads=0,
unset OpenMP environment, and ROS domains 212--214. The command supplied
--require-ape, --max-rtf 0.95, --max-peak-rss-mib 672, and
--max-wall-cv-percent 5.0. The complete receipt is
/tmp/lidarslam-m4b-mid360-stride5-gate.VZX2kH with execution identity
5e06bf535bbf75e1de53c122655e55bcbd4d459d7723989b4472de2e0c220781.
The runner, script, params, and bag-tree hashes were respectively
321b231d212ef804169f0dfb2aa509067000a40e2393aa66a2c0603f823c5357,
a7fa30b9a53ee635654360bb08992970a0dd79a5400235739ff9299647d1afc0,
534db7c3d9d41baa9d3bc95f9d2b87277479f9cf7c84b80e2182cfab45958cb6, and
408e6988cc7b1bb960000c66e798520b85ae7c0ffe29445394964f1751920016.
The machine receipt, YAML/JSON/Markdown summaries, and all three completion
markers report PASS. Wall time was 85.05/88.98/90.17 s (mean
88.066666667 s, CV 2.484173052%), RTF was
0.249230202/0.260746659/0.264233831, and peak RSS was
564.718750/565.035156/565.222656 MiB. The three-run artifact hashes were
identical:
loop_edges_sha256=5e30036d9190933e9659805caac032cc675ba968700a096c788f12560026c6d
trajectory_raw_sha256=19e0b2c1c4cf3ec85446fd6354f0014d26a0af5cbe7e03966f9b2de993360378
trajectory_optimized_sha256=9b8fd9faff62e7f8a20073373c7d85f5153bee0d18128a4e5ed957df351574ac
trajectory_refined_sha256=2f80eabff43ed11eb1760f1ff7cc165aa5240e3c2bf00b787354be9a0d875042
refinement_report_sha256=a02355cc8b8e02a888ae348ae2ec9528c5ee391c8bab8a1d96b290e4a5add1d4
registration_receipt_sha256=7c4f90c758abcba03fd7bae1f709364e496d47a11a68ebc33ae0d1d325d8822f
The receipt remained role=backend_loop, host lidarslam_builtin/NdtOmp,
and target_cell_cache_capacity=3 on every run. The accepted loop edge was
6 -> 604 with fitness 0.47313574856385182. Required APE completed with
the GLIM/reference trajectory proxy at 1.1072674021694795 m (218 pairs,
358 rejected); it is not ground truth or an absolute-accuracy/SOTA result.
This closes the pinned MID-360 development-profile performance and
determinism gate only. HILTI exp04/exp07, GT holdout, Humble/toolchain matrix,
and any global default or README claim remain separate pending gates. The
earlier M4a strict-RTF FAIL receipt is intentionally retained as history.
M4c HILTI backend regression gates (2026-08-21)¶
M4c extends the same fixed-run measurement contract to two HILTI backend-input
receipts without changing the default parameter profile or the absolute map
quality profile. Both gates used Jazzy, the current installed runner, unset
OpenMP variables, three isolated ROS domains, --require-ape, and the fixed
RTF/RSS/CV limits (0.95, 672 MiB, 5%).
The exp04 receipt is
/tmp/lidarslam-m4c-hilti-exp04-gate.pzufEB, execution identity
d991e3c17399cb6c7f974a79ac58321fbdfc179efc7c37d02d4b81ea2796765d. All
three runs are byte-identical and pass the performance gate: wall
2.71/2.90/2.73 s (CV 3.066357758%), maximum RTF 0.010347643, and peak
RSS 272.167968750 MiB. The optimized trajectory is the old artifact exactly
(SHA-256 3e94a3482b67e56de515cb830fcd374dd0b6cab5ffb7c150e1ee414c5922b390),
and the loop-edge SHA-256 is
4c90fb5921414b59a43d90c7c5ff9bef82681c77254fa48f9ba683dff8f3768d.
The old-map/current-map paired checker passes at the 2% budget. The
unchanged indoor_construction absolute profile still reports
mme_valid_fraction=0.816687654 for the old map and 0.816816915 for the
current map, both below 0.90; this is recorded as profile applicability,
not hidden by the paired pass.
The exp07 receipt is
/tmp/lidarslam-m4c-hilti-exp07-gate.zCELMb, execution identity
bd3cd4496f5c4c6c28d274397a632c497aac19a6c6911dda0cc3ff7e165ea67e. The
three-run machine YAML/JSON/Markdown summaries and completion markers all
report PASS. Wall time is 1.90/1.90/1.97 s (CV 1.715683698%), maximum
RTF 0.050166829, and peak RSS 201.136718750 MiB. The optimized trajectory
matches the old artifact exactly (SHA-256
353226fb6b7b8ff430dca063224fe3b059a1d44eace7bb32204044964620162a), with
loop edges SHA-256
4c90fb5921414b59a43d90c7c5ff9bef82681c77254fa48f9ba683dff8f3768d.
Historical interpolated APE is 0.6186851452574647 m from 5/6 sparse GT
points, matching the stated old value; one sparse reference point is
rejected by the 2 s matching limit. Each run records the canonical
backend_loop host NDT receipt and target_cell_cache_capacity=3.
These are compatibility, determinism, and resource-regression gates for the named HILTI inputs. They are not official dense-GT scores or SOTA/competitor comparisons. M5 must add the dense GT protocol, holdout datasets, and any toolchain matrix before a broader accuracy or production claim; README remains unchanged.
M2 startup preflight gate¶
The component-owned preflight is the smallest live integration slice. With the
selector disabled, the normal one-argument ScanMatcherComponent constructor
maps the legacy method name to a host-resident class, resolves it through the
same typed request, and commits a session before creating publishers or sensor
subscriptions. This preserves the built-in numerical/configuration defaults
without leaving a raw-PCL processing fallback. With the selector enabled, the
component instead resolves the explicit host or external class and validates
API major/minor, SPDX license, capabilities, typed configuration, method, and
variant before the same transactional activation. Unknown, unavailable,
mismatched, or invalid classes fail closed; no fallback or runtime hot reload
is allowed.
The parameters are read-only after construction:
| Parameter | Default | Gate |
|---|---|---|
registration_plugin_enable |
false |
Must be paired with a non-empty class ID. |
registration_plugin_class |
"" |
Explicit lidarslam_builtin/<name> host ID or external pluginlib ID. |
registration_plugin_allow_external |
false |
Required for external pluginlib DSOs; it is an explicit risk acceptance, not a fallback switch. |
The live frontend hot path calls only RegistrationPluginSession::align() and
setInputTarget() after activation. The backend live/offline shells share the
same session-owned boundary; a read-only source audit rejects direct raw-PCL or
raw-plugin processing calls in all three production shells. The 2026-08-28
Jazzy Release build and all three registration-focused scanmatcher test targets
pass, and the production-boundary source audit reports PASS. The
external success test remains wiring-only. The independent DSO replay, ODR,
optional-dependency, and Humble/Jazzy matrix gates remain No-Go, so this slice
does not permit README superiority claims.
M0 clean external consumer gate¶
The reproducible install-space check is
scripts/run_registration_plugin_consumer_check.sh.
It starts from /opt/ros/$ROS_DISTRO/setup.bash, copies the public interface and
shell loader into an isolated underlay, builds the fake external package in a
separate overlay with -DCMAKE_CXX_STANDARD=14, and runs the underlay loader
test after sourcing only those temporary installs. It does not source the
repository's install/setup.bash; --keep-work-dir preserves logs and the
receipt for inspection. The C++14 claim applies to the public interface and
external fake consumer; the shell loader itself remains C++17 as documented in
the ADR.
The required receipt fields are:
clean_external_consumer_proof=pass
ros_setup=/opt/ros/<distro>/setup.bash
base_packages=lidarslam_plugin_interfaces,lidarslam_registration_loader
external_package=lidarslam_fake_registration_plugins
external_cxx_standard=14
gtest_filter=RegistrationPluginLoader.DiscoversInstalledFakeExternalClasses:RegistrationPluginLoader.LoadsExternalPluginAndKeepsLoaderAlive
repository_install_sourced=false
The historical two-leg receipt claimed a pass with the two selected tests;
that evidence is superseded by the current-source four-leg release matrix and
must not be used for promotion. Jazzy was run from the local /opt/ros/jazzy
installation. The historical Humble proof used the read-only
repository mount and ros:humble-ros-core image digest
sha256:ebae805c9d985e443b26e13a47339098dc0a42eee4626055bfd4ebc6dcdb4988.
Its toolchain receipt was: GCC 11.4.0-1ubuntu1~22.04.3, PCL
1.12.1+dfsg-3build1, Eigen 3.4.0-2ubuntu2, and
ros-humble-pluginlib 5.1.4-1jammy.20260717.234837 from /opt/ros/humble.
The C++14 claim remains limited to the installed public interface and external
fake consumer; the shell loader is C++17.
The Humble receipt fields were:
clean_external_consumer_proof=pass
ros_setup=/opt/ros/humble/setup.bash
base_packages=lidarslam_plugin_interfaces,lidarslam_registration_loader
external_package=lidarslam_fake_registration_plugins
external_cxx_standard=14
gtest_filter=RegistrationPluginLoader.DiscoversInstalledFakeExternalClasses:RegistrationPluginLoader.LoadsExternalPluginAndKeepsLoaderAlive
repository_install_sourced=false
M1 external author template gate¶
The first author-facing SDK slice is deliberately a small, buildable package,
not a second algorithm implementation. The template at
examples/lidarslam_registration_plugin_template demonstrates the complete
metadata, capability, typed-configuration, target, alignment, reset, and
pluginlib manifest contract. Its identity alignment returns a transformed
aligned_source consistent with the requested initial guess; it makes no
accuracy or SOTA claim. The negative fake package remains separate and is used
only by loader failure tests.
The reproducible proof is:
It starts from /opt/ros/$ROS_DISTRO/setup.bash, copies only
lidarslam_plugin_interfaces and lidarslam_registration_loader into a clean
underlay, builds the template in a separate overlay, and runs both the direct
C++14 contract fixture and the C++17 shell-loader fixture. The template test
checks configure acceptance/rejection, finite target/source validation,
capability mismatch, enabled/disabled initial-guess behavior, aligned-cloud
consistency, and reset to kNotConfigured. The loader test destroys the
loader before using the retained session, and checks exact XML/metadata class
identity, SPDX license acceptance, manifest/library provenance, configuration,
alignment, and reset.
The local Jazzy receipt is:
m1_template_proof=pass
ros_setup=/opt/ros/jazzy/setup.bash
base_packages=lidarslam_plugin_interfaces,lidarslam_registration_loader
template_package=lidarslam_registration_plugin_template
template_class=lidarslam_registration_plugin_template/Identity
plugin_cxx_standard=14
loader_test_cxx_standard=17
metadata_class_id=lidarslam_registration_plugin_template/Identity
metadata_license=BSD-2-Clause
repository_install_sourced=false
The historical workflow ran this proof for both Humble and Jazzy after
dependency installation and before the normal workspace build. Those matrix
legs are superseded and do not satisfy the current release gate. The
historical Humble run used a read-only repository mount
and the same ros:humble-ros-core image digest recorded by M0
(sha256:ebae805c9d985e443b26e13a47339098dc0a42eee4626055bfd4ebc6dcdb4988).
The C++14 source claim applies to the public plugin and its direct consumer,
while the shell loader remains C++17.
1. Goal¶
Make lidarslam_ros2 a platform where an external contributor can add a
registration method, place recognizer, loop verifier, optimizer, map refiner,
or exporter without editing the central ROS components.
Success means:
- a useful extension can live in a separate ROS package and repository;
- the default
RKO-LIO + graph_based_slamworkflow remains unchanged; - online ROS components and deterministic offline runners use the same typed contract; explicit live startup preflight is implemented, while external live-plugin promotion remains pending its safety gates;
- invalid or incompatible plugins fail at startup with an actionable error;
- plugin performance and determinism are measurable through a shared contract test kit;
- permissive-license and provenance rules remain enforceable.
The first compatibility promise is source API and configuration stability within one major plugin API version. Cross-distribution C++ binary ABI compatibility is not promised: ROS 2, PCL, Eigen, and compiler ABIs differ between Humble/Jazzy and Ubuntu releases.
2. Current architecture and constraints¶
The repository already has strong foundations:
- composable ROS 2 nodes through
rclcpp_components; - a ROS-free, clock-free
BackendCoreand deterministic offline runners; - many pure logic headers with focused tests;
- release gates for byte determinism, APE, map quality, runtime, memory, and Autoware map verification.
The main extension bottlenecks are:
- the live scan-registration default remains a string-driven legacy path inside
ScanMatcherComponent; the typed runtime slot and host resolver now support explicit startup preflight and offline injection, while later call sites still require migration; - the live and offline loop-registration NDT paths now share a typed host session, canonical backend role/request, and receipt; the live and offline GICP compatibility paths retain explicit legacy construction until their separate resolver characterization;
- four place-recognition implementations and their databases are compiled
directly into
BackendCore, whose public getters expose their concrete database types; - loop verification, pose-graph optimization, map refinement, and export are callable modules but not external extension boundaries;
- the registration role now has an installed versioned source API, capability negotiation, plugin manifests, and shell contract tests; the equivalent kit does not yet exist for backend, recognition, optimization, refinement, or export roles;
- configuration is distributed across large YAML parameter sets, so adding an algorithm currently adds central parameters and branches.
3. Design rules¶
- Functional core, ROS shell, replaceable algorithms. Plugin interfaces
remain free of
rclcpp::Node, publishers, subscriptions, filesystems, and wall-clock scheduling. The shell owns ROS and I/O. - One ordered input, one deterministic result. Offline runners and live components call the same implementation and data contract.
- Plugin discovery only at startup. The processing hot path contains no dynamic lookup and no parameter reads.
- Small role-specific interfaces. Do not create a universal
SlamPluginor expose the entire component state. - Capabilities are explicit. A plugin declares required point fields, initial-guess support, covariance support, deterministic mode, thread model, and optional sensor requirements before processing starts.
- Built-ins migrate before third-party plugins. Each seam first wraps the current implementation and proves byte-identical or tolerance-bound output.
- Rule of two. Extract a new shared interface only when the built-in path and at least one second implementation exercise it. Avoid speculative seams.
- Fail closed. Unknown class IDs, API-version mismatches, missing point fields, invalid parameters, and unsupported capabilities are startup errors.
- No hidden dataset branches. Plugins receive geometry and declared sensor data, never dataset or sequence identity.
- Load outside the deterministic core. ROS shells and offline runners own
pluginlib::ClassLoader, validate manifests, and inject already-created interfaces.BackendCorenever depends on pluginlib or performs discovery.
4. Target package and runtime shape¶
The target dependency direction is:
lidarslam_plugin_interfaces (C++14 types, abstract roles, API version; no rclcpp)
^
|
lidarslam_default_plugins (NDT/GICP, built-in descriptors, verifier, g2o,
| map refinement/export adapters)
|
scanmatcher / graph_based_slam cores
^
|
ROS components and offline runners
Initially, interfaces may remain inside their existing package to keep PRs
small. Move them to lidarslam_plugin_interfaces only after downstream
install-space tests prove the boundary. Default implementations may likewise
remain in scanmatcher or graph_based_slam until the interface is stable.
Every plugin role shares these base concepts:
PluginApiVersion {major, minor};PluginMetadata {id, implementation_version, license, capabilities};- immutable configuration created at startup;
- typed request/result structures with explicit validity and diagnostics;
reset()semantics and a declared thread model;- structured timing, iteration, convergence, and failure diagnostics.
The common interface stays at C++14 because graph_based_slam currently uses
C++14, even though scanmatcher can use C++17 internally. The host records the
loaded class ID, implementation version, API version, library identity,
capabilities, and configuration hash in each run manifest.
The configuration shape becomes:
registration:
plugin: lidarslam_default_plugins/NdtOmp
parameters:
resolution: 1.0
maximum_iterations: 100
Legacy parameters such as registration_method: NDT map to the equivalent
built-in plugin for two minor releases and emit one deprecation warning.
5. Extension roles and priority¶
| Priority | Role | Minimal contract | Why this order |
|---|---|---|---|
| P0 | RegistrationPlugin |
configure, set target, align source with initial guess, return pose/fitness/convergence/diagnostics/capabilities | Existing duplicate factories and contributor demand make this the safest first seam. |
| P1 | PlaceRecognitionPlugin |
ingest immutable submap, query ranked candidates, serialize deterministic diagnostics | Four built-ins are directly coupled to BackendCore. |
| P1 | LoopVerifierPlugin |
verify one candidate and return a constraint or typed rejection | Enables 3D-BBS and future verification without central branching. |
| P2 | GraphOptimizerPlugin |
optimize typed nodes/constraints under explicit gauge and robust-kernel config | Allows g2o alternatives after graph semantics are frozen. |
| P2 | MapRefinerPlugin |
refine immutable submaps/poses and return poses plus quality diagnostics | Existing refiner is already close to a pure boundary. |
| P2 | map-export strategy | consume a frozen map snapshot and emit a declared artifact manifest | Start as a normal C++ strategy; promote it to pluginlib only after a real external exporter requires it. |
| P3 | FrontendPlugin |
ordered LiDAR/IMU events to odometry/submap events | High value but too broad until registration and map-update contracts stabilize. |
| P3 | sensor adapters | normalize vendor messages into canonical point/IMU events | Prefer separate adapter nodes first; use in-process plugins only with measured need. |
Candidate aggregation policy, safety gates, loop-edge deduplication, scheduling, and benchmark scoring remain core policy rather than plugins. Allowing plugins to replace the judge would weaken reproducibility and safety.
Registration capabilities replace NDT-specific casts and branches. They cover, at minimum, rotation/translation priors, adaptive correspondence controls, mean-correspondence diagnostics, target preprocessing, covariance, and deterministic execution. Requesting an unsupported capability is a startup configuration error rather than a silently ignored option.
6. Compatibility and lifecycle policy¶
- Version plugin interfaces independently from the application release.
- A major API mismatch is a hard startup failure. A newer minor capability is accepted only when the host can ignore it safely.
- Changing an existing pure virtual signature requires a new major plugin API.
- Configuration keys are namespaced by plugin ID. Renames keep aliases and migration warnings for at least two minor application releases.
- ROS messages and artifact JSON/YAML retain explicit
schema_versionfields. - Plugin manifests declare license and upstream source. The default workflow loads only plugins allowed by the permissive-license policy.
- The core never catches an arbitrary plugin exception and continues with partially mutated state. Each call either returns a valid result or a typed failure; fatal exceptions terminate that processing session cleanly.
7. Delivery phases and hard gates¶
Phase 0 — contracts and characterization¶
Deliver:
- architecture decision record for plugin boundaries and compatibility;
- inventory of current algorithms, parameters, dependencies, licenses, and ROS/offline call sites;
- frozen registration request/result fixtures for NDT and GICP;
- downstream install-space test skeleton and example consumer package.
Gate: no production behavior change; existing CI and release gates pass; the contract can represent every result currently used by scanmatcher and loop registration without exposing ROS component state.
Phase 1 — registration seam¶
Deliver:
- one shared
RegistrationPlugininterface andpluginlibloader; - built-in NDT and GICP adapters, then optional FAST_GICP/small_gicp adapters;
- identical selection semantics in scanmatcher, backend, and offline runners;
- legacy parameter adapter and clear startup diagnostics;
- external
example_registration_pluginbuilt in a separate workspace.
Gate:
- default NDT outputs are byte-identical on deterministic fixtures and replay;
- all existing registration methods pass their current accuracy gates;
- plugin dispatch adds less than 0.5% processing overhead;
- end-to-end RTF remains within 5% of the corresponding baseline;
- missing, incompatible, and throwing plugins pass negative tests;
- Humble and Jazzy install-space consumer builds pass.
Phase 2 — loop pipeline seams¶
Deliver:
PlaceRecognitionPluginandLoopVerifierPlugin;- adapters for Scan Context, BEV, SOLiD, Triangle, distance fallback, and 3D-BBS;
- deterministic candidate ordering independent of plugin discovery order;
- per-plugin budgets and diagnostics, while the core retains aggregation and acceptance policy.
Scan Context is the pilot migration. BEV and Triangle move later because their current cross-checking creates a more complicated dependency boundary. Concrete descriptor-database getters are deprecated only after equivalent contract tests cover their behavior.
Gate:
- frozen descriptor databases and accepted loop-edge sets are byte-identical;
- plugin ordering cannot change the result;
- false-loop, timeout, memory, and deterministic replay gates pass;
- a standalone example descriptor can be installed without editing the core.
Phase 3 — optimization, refinement, and export¶
Deliver:
- immutable graph, map snapshot, constraint, and artifact-manifest contracts;
- adapters for the existing g2o optimizer, clean-room map refiner, and Autoware map-export strategy;
- one small alternative/reference implementation per seam for contract tests.
Gate:
- default trajectory and map hashes remain unchanged where byte identity is promised;
- exporters cannot mutate estimator state;
- map-quality, Autoware verification, resource, and license gates pass;
- failed exporters leave no partially valid artifact manifest.
Phase 4 — frontend and sensor ecosystem¶
Deliver only after the earlier contracts have survived at least one release:
- typed ordered LiDAR/IMU event contracts;
- frontend lifecycle and state-handoff contract;
- external frontend adapter example;
- documented adapter-node path for new LiDAR vendors and point-time layouts.
Gate:
- RKO-LIO remains the unchanged default;
- frontend determinism and complete map-authoring E2E pass;
- capability checks reject missing per-point time, calibration, or IMU data before mapping begins;
- no dataset-specific branch enters the plugin API.
Phase 5 — contributor SDK and ecosystem release¶
Deliver:
create_lidarslam_pluginscaffold command or template repository;- contract-test CMake helper and reusable CI workflow;
- extension cookbook, API reference, compatibility matrix, and migration guide;
- plugin proposal issue form with license, capability, benchmark, and maintainer fields;
- curated registry listing compatibility and evidence, not an automatic trust store.
Gate: a contributor unfamiliar with the core can generate, build, test, install, discover, and run an example plugin from a clean workspace using only published documentation. Target time: under 30 minutes.
8. First two-week sprint¶
Keep the first sprint behavior-preserving and small:
- Write the registration API decision record and parameter mapping table.
- Add characterization tests around the current scanmatcher and loop registration factories.
- Define typed registration request/result/diagnostic structures with no ROS dependencies.
- Wrap NDT behind the interface without dynamic loading and prove identical output first.
- Add the
pluginlibloader only after that proof. - Build one separate-workspace identity/example plugin in CI.
- Publish a short “add a registration plugin” guide.
Sprint exit: the default YAML still works, deterministic replay remains identical, the example plugin loads from an installed external package, and no new algorithm-specific branch was added to either central component.
9. OSS health metrics¶
Track architecture and community outcomes together:
| Metric | Initial target |
|---|---|
| Core-file edits required for a new plugin | 0 |
| External example plugin build matrix | Humble + Jazzy |
| Plugin contract-test pass rate | 100% |
| Default deterministic replay regression | 0 bytes |
| Plugin dispatch runtime overhead | < 0.5% |
| Documented plugin setup time | < 30 min |
| Deprecated configuration window | >= 2 minor releases |
| Unversioned public artifact schemas | 0 |
These targets do not replace SLAM quality gates. A plugin can be easy to add and still be rejected from the default workflow for accuracy, runtime, memory, map quality, determinism, license, or maintenance reasons.
10. Risks and explicit non-goals¶
- Over-abstraction: require a second real implementation before extracting each seam and keep policy in the core.
- C++ ABI fragility: promise source compatibility, publish a build matrix, and reject incompatible API majors at startup.
- Parameter sprawl: namespace plugin parameters and validate them once at startup; do not copy all plugin knobs into central YAML schemas.
- Nondeterministic plugin behavior: provide deterministic fixtures and mark capabilities honestly; deterministic offline mapping may reject plugins that cannot satisfy the contract.
- License contamination: manifests, CI checks, and default-workflow policy remain mandatory; loading code dynamically does not remove license duties.
- Unsafe fallback: never silently substitute another algorithm after a plugin fails to load or fails mid-run.
Non-goals for the first three phases are a distributed plugin marketplace, runtime hot-swapping during a map, a universal point type, stable C++ binary ABI across ROS distributions, and replacing ROS 2 composition itself.