ndt_omp release-review audit — 2026-08-12
This is a read-only audit of the dependency blocking the normal
apt install ros-<distro>-lidarslam path. It corrects the earlier
wait-only interpretation of the two generated rosdistro pull requests.
Result
The latest authenticated run of
python3 scripts/check_ndt_omp_release_readiness.py --json reports
BLOCKED: both generated PRs have an unanswered lineage review and one
failed check run at their exact heads.
- Humble PR #52949 is open,
non-draft, and mergeable at
c375b1c8e92d14e58a4c10e023920763645fe5c7. - Jazzy PR #52950 is open,
non-draft, and mergeable at
ef7e147af917eee64f4569a528dd98400004cadd. - The latest unanswered human review asks how this package relates to the
existing
ndt_omppackage: direct question, matching Humble question. - The source tag and Bloom release repository already exist. Recreating the tag or rerunning Bloom does not answer the review.
- Each exact head has six check runs: four successful, one neutral, and one
failed
rosdistro / rosdep checks (3.8)run. Neutral is non-blocking, so the machine report records 5/6 passing and 1/6 failing for each PR.
No GitHub comment, PR, tag, repository, or distribution file was changed by this audit.
Read-only state refresh — 2026-08-15
An authenticated, thread-aware read-only rerun found no inline review threads
on either PR. The actionable feedback is instead a human review body on Jazzy
asking how the package relates to ndt_omp; the Humble review points to that
same question. Both remain unanswered, both PRs remain open at the same exact
heads above, and GitHub currently reports mergeable=true. Mergeability only
records the current base-conflict calculation; it does not grant reviewer
approval or make the colliding packages release-ready.
The checker now sends an optional GITHUB_TOKEN only to api.github.com,
binds every check run to the PR's exact head SHA, prints check totals beside
mergeability, and fails closed for failed, pending, missing, or truncated
check-run evidence. A CI blocker does not hide a pending human-review action.
Focused tests cover authenticated request scoping, explicit conflicts,
unknown mergeability, all check-run gate states, retained review actions, and
readable output. No external state was changed by the refresh.
Exact-head CI classification
| PR | Exact-head check result | Failing job |
|---|---|---|
| Humble #52949 | 5/6 passing, 0 pending, 1 failing | rosdistro / rosdep checks (3.8) |
| Jazzy #52950 | 5/6 passing, 0 pending, 1 failing | rosdistro / rosdep checks (3.8) |
Both failed logs report 1 failed and 13 passed tests. The only failure is the
OpenEmbedded lookup for libpcre@openembedded-core; it does not inspect or
reject the 15-line ndt_omp_ros2 registration itself. ros/rosdistro
PR #52858 subsequently moved
that key to libpcre@meta-ros-common, current master contains the corrected
rule, and a later
Validate rosdistro run
passes. Each old NDT branch has one candidate commit absent from master,
while current master has 307 commits absent from that branch.
It is therefore a supported inference that these two failures come from the
stale branch base and global rosdep state, not the NDT YAML delta. This does
not turn either exact head green. Do not spend effort refreshing the colliding
registration as-is: select the canonical or fully isolated path first, then
refresh or recreate the generated PR from current rosdistro master and
require a complete green suite.
Relationship to the existing package
ndt_omp_ros2 is a downstream ROS 2 port/fork of
koide3/ndt_omp, not an independent
registration implementation. The fork README says this explicitly, and its
history contains the original pclomp implementation.
The current upstream package already supports ROS 2 and exports the same
algorithm family. Humble rosdistro registers and releases ndt_omp version
0.0.0-1; Jazzy does not currently register it. The upstream Hessian
correction is also already merged as
koide3/ndt_omp#46.
The product-specific delta that scanmatcher currently consumes is fork
commit d435e32:
- rotation-prior setters and clearing;
- translation-prior setters and clearing;
- maximum NDT correspondence-distance filtering;
- mean correspondence-distance reporting after alignment.
scanmatcher/src/scanmatcher_component.cpp calls all four API groups for its
opt-in IMU NDT prior, z prior, and adaptive correspondence threshold.
Buildfarm metadata, target export, installed-consumer tests, and Humble/Jazzy
CI are release-quality improvements, but they do not make the implementation
a separate algorithm.
Co-installation audit
The current fork is not safely co-installable with Humble's released
ndt_omp, even though its ROS package and CMake package are named
ndt_omp_ros2.
| Surface | Existing ndt_omp |
Candidate ndt_omp_ros2 |
Result |
|---|---|---|---|
| ROS package | ndt_omp |
ndt_omp_ros2 |
distinct |
| CMake package | ndt_omp |
ndt_omp_ros2 |
distinct |
| C++ namespace | pclomp |
pclomp |
collision |
| installed headers | include/pclomp/* |
include/pclomp/* |
file-ownership collision |
| shared library | lib/libndt_omp.so |
lib/libndt_omp.so |
file/SONAME collision |
| algorithm lineage | koide3 implementation | downstream fork | overlapping implementation |
The two Debian packages can therefore claim the same files under
/opt/ros/humble. A unique repository key alone is not a sufficient
co-installation boundary. The current rosdistro PRs must not be described as
merge-ready until this is resolved with the reviewer.
Convergence decision
The preferred durable path is upstream convergence:
- Submit the four product-required APIs with focused tests to
koide3/ndt_omp. - Change both direct consumers,
scanmatcherandgraph_based_slam, to depend on the canonicalndt_omppackage and exported target. - Coordinate a Humble update and first Jazzy release of that canonical package.
- Close or supersede the current
ndt_omp_ros2registrations; never install overlapping headers and libraries under a second Debian package name.
If upstream explicitly declines the project-specific APIs, the fallback is a
fully isolated fork. That requires a new package identity plus distinct C++
namespace, include directory, library/SONAME, CMake target, executable path,
version/tag, Bloom tracks, and rosdistro PRs. Merely renaming package.xml is
not enough.
This decision prioritizes one maintained implementation and normal ROS package resolution over a project-specific duplicate. Source and Docker onboarding remain available while that external coordination proceeds.
Prepared and verified convergence implementation
The upstream-first path is no longer only a proposal. A local patch was
prepared against exact koide3/ndt_omp commit
5495fd9214945afcb4b35d5a1da385e405c52bf9:
- artifact:
packaging/ndt_omp/koide3-ndt-omp-lidarslam-priors.patch; - SHA-256:
7b641c32ec4f30faa302e60aaa89765bb9acf67f3f0feb85f9e4e11e88b4dc9f; - payload: rotation and per-axis translation priors, correspondence-distance filtering, accepted-correspondence diagnostics, input sanitization, and four focused GTests;
- implementation detail: correspondence statistics use the upstream per-input-point reduction order, preserving its deterministic summation rather than transplanting the fork's thread-local accumulator.
The checked-in contract and read-only checker bind the exact upstream commit, patch hash, five-path inventory, API markers, test names, parent-consumer inventory, and no-write authority boundary:
python3 scripts/check_canonical_ndt_convergence.py --json
python3 scripts/check_canonical_ndt_convergence.py \
--upstream-checkout /path/to/clean/koide3-ndt_omp \
--require-ready-for-upstream-review
GITHUB_TOKEN="$(gh auth token)" \
python3 scripts/check_canonical_ndt_convergence.py \
--upstream-checkout /path/to/clean/koide3-ndt_omp-at-5495fd9 \
--candidate-checkout /path/to/clean/ndt_omp-at-618f02f \
--online \
--require-ready-for-draft-pr
The strict command reported READY_FOR_UPSTREAM_REVIEW with 20/20 checks
against a clean detached checkout of that exact commit. The report intentionally
omits the checkout path. This state means the local bundle is reviewable; it
does not authorize or claim an upstream PR. The stronger online command then
reported READY_FOR_DRAFT_PR with 30/30 checks, binding exact candidate commit
618f02f, its exact parent/subject/diff hash, current upstream master, fork
identity, proposed-branch absence, and a zero-match search across four open
upstream PRs. It omits both checkout paths and still reports
github_writes_authorized=false and remote_mutations_performed=false.
The exact patch was also fixed as local upstream commit
618f02f6b50a8590b81f48b4fee5b6cfc8d3f3ea and rebuilt from a clean Jazzy
worktree. The copy-ready Draft PR title/body, duplicate check, exact fork
branch, validation result, publication order, and no-write boundary are in the
upstream PR packet.
The commit is local-only and is not a publicly resolvable revision.
The parent boundary was then tested with a copied scanmatcher package that
depends on canonical ndt_omp. That first exposed two additional downstream
compatibility deltas: the fork-only setOulierRatio misspelling and direct
use of Boost shared pointers after modern PCL changed its Ptr alias. The
parent source now uses pcl::Registration::Ptr, pcl::make_shared, and
pcl::dynamic_pointer_cast in both frontend and loop-registration paths;
the existing ndt_omp_ros2 build remains compatible with that modernization.
The remaining canonical switch is captured separately as
packaging/ndt_omp/lidarslam-canonical-ndt-transition.patch (SHA-256
c090b8f2228b21dcf30650114f9638f38497ca5a0214e3e6063a53aa7bef66b1).
The earlier three-file draft was incomplete: graph_based_slam also directly
declared canonical-provider requirements in its manifest and seven CMake
locations. The corrected five-file patch replaces all eleven direct
dependency references across both consumers and corrects the fork-only
outlier-setter spelling. The fail-closed checker compares those counts with
the live parent files and rejects omission or drift.
Verification completed locally:
- canonical upstream
ndt_omp: Humble and Jazzy build/install passed; all 4/4 new GTests passed on both distributions; - complete canonical transition:
ndt_omp,lidarslam_msgs,scanmatcher, andgraph_based_slambuilt and installed from the same staged source on network-isolated immutable Humble and Jazzy images; - installed manifests and both consumer CMake caches resolve
ndt_ompfrom the new prefix; nondt_omp_ros2package/share or dependency remains; - the convergence checker has 11/11 regressions passing, including artifact drift, graph-consumer omission, dirty/wrong upstream checkout, apply failure, private-path exclusion, create-only output, and authority rejection;
- the complete maintained product Python gate passes 2,098 tests
(
graph_based_slam1,428 andlidarslam670), with 13 known skips and no failures; - the earlier canonical
scanmatchertest run remains 109/109 passing; - current fork + modernized parent (
ndt_omp_ros2,scanmatcher, andgraph_based_slam): all three packages built and installed from clean build directories; - the first full parent run passed 227/232 CTest targets. The five failures were two free-space safety gates after temporary builds reduced free space below 5 GiB, plus three pre-existing lint findings in newly added map-edit files. The generated build/install directories were removed, the lint findings were corrected, and all five failed targets passed their focused reruns (15/15 functional cases plus copyright, cpplint, and flake8).
These artifacts were prepared but not submitted. No upstream PR, review reply, branch push, tag, Bloom run, or rosdistro mutation was performed.
Prepared reviewer responses
These responses are prepared but were not posted. The Jazzy response is the full answer to the direct question:
Thanks for catching this.
ndt_omp_ros2is a downstream ROS 2 fork ofkoide3/ndt_omp, not an independent implementation. It carries four APIs used by lidarslam's optional IMU/translation priors and adaptive correspondence threshold, plus buildfarm packaging work. However, the current candidate still installs the sameinclude/pclomp/*headers andlibndt_omp.soas Humble's releasedndt_omp, so the differently named Debian packages are not safely co-installable. I do not want these PRs merged as-is. My preferred correction is to upstream the required APIs and consume/release the canonicalndt_omppackage for Humble and Jazzy. The focused upstream work is tracked in Draft PR<UPSTREAM_PR_URL>. If those project-specific APIs are declined upstream, I will instead fully namespace the fork's package, headers, symbols, library, and CMake target, then replace these Bloom registrations. I will report the selected collision-free path here before requesting another merge review. The current red rosdep check also remains a hard gate; any replacement registration will be generated from current rosdistromasterand must be fully green.
The Humble response deliberately points to the same resolution instead of duplicating a divergent explanation:
The same lineage and co-installation issue applies here as in Jazzy
52950.
ndt_omp_ros2is a downstream fork that overlaps the canonical
ndt_ompheaders and library, so please do not merge this registration as-is. I am pursuing the collision-free canonical path in Draft PR<UPSTREAM_PR_URL>and will return with the accepted resolution before requesting another rosdistro review. Any replacement PR will be generated from current rosdistromasterand must pass its complete check suite.
Do not post either response while the literal <UPSTREAM_PR_URL> placeholder
remains, or unless that URL resolves to the Draft upstream PR containing exact
candidate commit 618f02f6b50a8590b81f48b4fee5b6cfc8d3f3ea (or a deliberately
reviewed successor).
Posting either response or changing either external PR requires an explicit maintainer publication decision.
Fail-closed response packet gate — 2026-08-16
The release checker now turns the prepared prose above into a copy-ready packet only after a second read-only identity gate. It requires all of the following at the same time:
- the online release audit completed without remote-inspection errors;
- both rosdistro PRs remain open at exact heads
c375b1c...andef7e147..., with the exact unanswered review URLs recorded above; - the canonical URL resolves to an open Draft PR targeting
koide3/ndt_omp:masterfrom the expected fork; - the Draft head is exact candidate commit
618f02f6...; and - no rosdistro response has already superseded the recorded review state.
If any condition fails, packet status is BLOCKED, both response bodies are
null, and the human output says No copy-ready response was emitted. A red
rosdistro check does not suppress the lineage response because that response
explicitly preserves green CI as a separate hard gate. It still prevents the
registration itself from being described as release-ready.
Current no-PR inspection is intentionally blocked:
python3 scripts/check_ndt_omp_release_readiness.py \
--review-response-packet \
--json
After the canonical Draft exists, a maintainer can bind and inspect the exact copy without posting it:
GITHUB_TOKEN="$(gh auth token)" \
python3 scripts/check_ndt_omp_release_readiness.py \
--review-response-packet \
--upstream-pr-url "${verified_upstream_pr}" \
--require-review-response-ready \
--json
The packet always reports github_writes_authorized=false and
remote_mutations_performed=false. Focused regression coverage is 25/25,
including exact-target unlock, missing URL, rosdistro-head drift, non-Draft
upstream state, wrong repository URLs, and strict-gate refusal. The live
canonical preflight was also refreshed on 2026-08-16 from clean temporary
worktrees and remains READY_FOR_DRAFT_PR, 30/30 checks, with the proposed
branch absent and zero semantic duplicate PRs. No GitHub state was changed.